├── .nvmrc
├── .bowerrc
├── web
├── config.js
├── app.js
├── templates
│ ├── basic.html
│ ├── userProfile.html
│ ├── login.html
│ ├── homepage_detail.html
│ ├── create.html
│ └── managePage.html
├── js
│ ├── lib
│ │ ├── Logout.js
│ │ ├── Rice.js
│ │ ├── UserSession.js
│ │ └── User.js
│ ├── Model
│ │ ├── RiceModel.js
│ │ ├── UserLogin.js
│ │ ├── CreateAccountModel.js
│ │ ├── CreateRiceModel.js
│ │ └── UserModel.js
│ ├── CreateAccountView.js
│ ├── HomeView.js
│ ├── UserProfileView.js
│ ├── LoginView.js
│ └── AdminView.js
├── test
│ └── spec
│ │ ├── UserLoginSpec.js
│ │ ├── CreateAccountViewSpec.js
│ │ ├── routerSpec.js
│ │ ├── LoginViewSpec.js
│ │ ├── UserSessionSpec.js
│ │ └── RiceModelSpec.js
├── main.js
├── index.html
├── lib
│ └── require
│ │ ├── async.js
│ │ └── json.js
├── router.js
└── css
│ └── pricing.css
├── .npmignore
├── test
├── mocha.opts
├── common.js
├── services
│ ├── rice_test.js
│ ├── authenticate_test.js
│ └── create_account_test.js
├── mapper
│ └── db_mapper_test.js
├── rest
│ └── rest_test.js
└── system
│ └── function_test.js
├── .gitignore
├── .travis.yml
├── .jslint.conf
├── database.json
├── migrations
├── 20141021181116-create-rice.js
├── 20141021180444-create-user.js
├── 20141023042211-update-user.js
├── 20141021173743-add-user.js
└── 20141021173924-add-rice.js
├── server
├── mapper
│ ├── db_prototype.js
│ ├── db_helper.js
│ ├── rice_mapper.js
│ └── account_mapper.js
├── service
│ ├── account_service.js
│ ├── service_helper.js
│ ├── rice_service.js
│ └── authenticate.js
└── app.js
├── bower.json
├── README.md
├── Gruntfile.js
└── package.json
/.nvmrc:
--------------------------------------------------------------------------------
1 | 0.10.32
2 |
--------------------------------------------------------------------------------
/.bowerrc:
--------------------------------------------------------------------------------
1 | {
2 | "directory": "web/lib",
3 | "analytics": false
4 | }
--------------------------------------------------------------------------------
/web/config.js:
--------------------------------------------------------------------------------
1 | define({
2 | localhost: "http://0.0.0.0:8080"
3 | });
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | dev.db
2 | .jslint.conf
3 | .travis.yml
4 | coverage/
5 | web/lib
6 | .grunt
7 | _SpecRunner.html
--------------------------------------------------------------------------------
/test/mocha.opts:
--------------------------------------------------------------------------------
1 | --reporter dot
2 | --ui bdd
3 | --growl
4 | --check-leaks
5 | --colors
6 | --require test/common
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .idea
3 | npm-debug.log
4 | dev.db
5 | coverage/
6 | web/lib
7 | .grunt
8 | _SpecRunner.html
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - "0.10.32"
4 |
5 | notifications:
6 | email: false
7 |
8 | before_script:
9 | - db-migrate up
10 |
--------------------------------------------------------------------------------
/test/common.js:
--------------------------------------------------------------------------------
1 | global.expect = require("chai").expect;
2 | global.assert = require("chai").assert;
3 | global.should = require('chai').should;
4 |
5 | require("../server/app");
--------------------------------------------------------------------------------
/.jslint.conf:
--------------------------------------------------------------------------------
1 | {
2 | "nomen": true,
3 | "debug": true,
4 | "evil": false,
5 | "vars": true,
6 | "unparam": true,
7 | "sub": true,
8 | "white": true,
9 | "regexp": true,
10 | "esnext": true,
11 | "predef" : ["define", "Backbone", "window"]
12 | }
--------------------------------------------------------------------------------
/database.json:
--------------------------------------------------------------------------------
1 | {
2 | "dev": {
3 | "driver": "sqlite3",
4 | "filename": "dev.db"
5 | },
6 |
7 | "test": {
8 | "driver": "sqlite3",
9 | "filename": "dev.db"
10 | },
11 |
12 | "prod": {
13 | "driver": "sqlite3",
14 | "filename": "dev.db"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/web/app.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | define([
4 | 'jquery',
5 | 'underscore',
6 | 'backbone',
7 | 'router'
8 | ], function($, _, Backbone, Router){
9 |
10 | var initialize = function(){
11 | this.router = new Router();
12 | };
13 |
14 | return {
15 | initialize: initialize
16 | };
17 | });
18 |
--------------------------------------------------------------------------------
/web/templates/basic.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/web/js/lib/Logout.js:
--------------------------------------------------------------------------------
1 | define([
2 | 'jquery',
3 | 'underscore',
4 | 'mustache',
5 | 'js/lib/User',
6 | 'js/lib/UserSession'
7 | ],function($, _, Mustache, User, UserSession){
8 | 'use strict';
9 | var Logout = function(){
10 |
11 | };
12 |
13 | Logout.prototype.logout = function(){
14 | UserSession.remove();
15 | };
16 |
17 | return Logout;
18 | });
--------------------------------------------------------------------------------
/web/templates/userProfile.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | | Name |
5 | email |
6 |
7 |
8 | {{#.}}
9 |
10 |
11 | | {{name}} |
12 | {{email}} |
13 |
14 |
15 | {{/.}}
16 |
--------------------------------------------------------------------------------
/migrations/20141021181116-create-rice.js:
--------------------------------------------------------------------------------
1 | var dbm = require('db-migrate');
2 | var type = dbm.dataType;
3 |
4 | exports.up = function(db, callback) {
5 | db.insert('rice',
6 | ['name', 'type', 'price', 'quantity', 'description'],
7 | ["Rice","Good", 12, 1 , "Made in China"],
8 | callback)
9 | };
10 |
11 | exports.down = function(db, callback) {
12 |
13 | };
14 |
--------------------------------------------------------------------------------
/migrations/20141021180444-create-user.js:
--------------------------------------------------------------------------------
1 | var dbm = require('db-migrate');
2 | var type = dbm.dataType;
3 |
4 | exports.up = function(db, callback) {
5 | db.insert('user',
6 | ['name', 'password', 'email', 'role','enabled'],
7 | ["admin","admin", "admin@phodal.com", 'user', 1],
8 | callback)
9 | };
10 |
11 | exports.down = function(db, callback) {
12 |
13 | };
14 |
--------------------------------------------------------------------------------
/web/js/Model/RiceModel.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | define(['backbone', "config"], function(Backbone, config) {
4 | var RiceModel = Backbone.Model.extend({});
5 | var Rices = Backbone.Collection.extend({
6 | model: RiceModel,
7 | url: config.localhost + '/all/rice',
8 | parse: function (data) {
9 | return data;
10 | }
11 | });
12 | return Rices;
13 | });
--------------------------------------------------------------------------------
/migrations/20141023042211-update-user.js:
--------------------------------------------------------------------------------
1 | var dbm = require('db-migrate');
2 | var type = dbm.dataType;
3 |
4 | exports.up = function(db, callback) {
5 | var updateAdminSql = 'UPDATE USER SET PASSWORD = ? WHERE NAME = "admin"';
6 |
7 | db.runSql(updateAdminSql, ['$2a$10$Vq/cxG7Nxai6SN5U4k.tQOrVySVf840wcmwFkbhXd5u9DQj5fwUiO'], callback);
8 | };
9 |
10 | exports.down = function(db, callback) {
11 |
12 | };
13 |
--------------------------------------------------------------------------------
/web/js/Model/UserLogin.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | define(["backbone", "config"], function(Backbone, config) {
4 | var LoginAccount = Backbone.Model.extend({
5 | defaults: {
6 | name: null,
7 | password: null
8 | },
9 | url: function () {
10 | return config.localhost + '/login/user';
11 | }
12 | });
13 |
14 | return LoginAccount;
15 | });
--------------------------------------------------------------------------------
/web/js/Model/CreateAccountModel.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | define(["backbone", "config"], function(Backbone, config) {
4 | var CreateAccount = Backbone.Model.extend({
5 | defaults: {
6 | name: null,
7 | email: null,
8 | password: null
9 | },
10 | url: function() {
11 | return config.localhost + '/account/create';
12 | }
13 | });
14 |
15 | return CreateAccount;
16 | });
17 |
--------------------------------------------------------------------------------
/web/templates/login.html:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
--------------------------------------------------------------------------------
/web/js/Model/CreateRiceModel.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | define(["backbone", "config"], function(Backbone, config) {
4 | var CreateRice = Backbone.Model.extend({
5 | defaults: {
6 | name: null,
7 | type: null,
8 | price: null,
9 | quantity: null,
10 | description: null
11 | },
12 | url: function() {
13 | return config.localhost + '/rice/create';
14 | }
15 | });
16 |
17 | return CreateRice;
18 | });
--------------------------------------------------------------------------------
/migrations/20141021173743-add-user.js:
--------------------------------------------------------------------------------
1 | var dbm = require('db-migrate');
2 | var type = dbm.dataType;
3 |
4 | exports.up = function(db, callback) {
5 | db.createTable('user', {
6 | id: { type: 'int', primaryKey: true },
7 | name: type.STRING,
8 | password: type.STRING,
9 | email: type.STRING,
10 | role: type.STRING,
11 | enabled: type.BOOLEAN
12 | }, callback);
13 | };
14 |
15 | exports.down = function(db, callback) {
16 | db.dropTable('user', callback);
17 | };
18 |
--------------------------------------------------------------------------------
/migrations/20141021173924-add-rice.js:
--------------------------------------------------------------------------------
1 | var dbm = require('db-migrate');
2 | var type = dbm.dataType;
3 |
4 | exports.up = function(db, callback) {
5 | db.createTable('rice', {
6 | id: { type: 'int', primaryKey: true },
7 | name: type.STRING,
8 | type: type.STRING,
9 | price: type.DECIMAL,
10 | quantity: type.DECIMAL,
11 | description: type.STRING
12 | }, callback);
13 | };
14 |
15 | exports.down = function(db, callback) {
16 | db.dropTable('rice', callback);
17 | };
18 |
--------------------------------------------------------------------------------
/web/test/spec/UserLoginSpec.js:
--------------------------------------------------------------------------------
1 | define([
2 | 'js/Model/CreateAccountModel'
3 | ], function(CreateAccount) {
4 | 'use strict';
5 |
6 | beforeEach(function() {
7 | this.user = new CreateAccount({
8 | name: "phodal",
9 | password: "password"
10 | });
11 | });
12 |
13 | describe("User Login", function() {
14 | it("should return user name", function() {
15 | expect(this.user.get('name')).toEqual("phodal");
16 | });
17 | it("should return user password", function() {
18 | expect(this.user.get('password')).toEqual("password");
19 | });
20 | });
21 | });
22 |
--------------------------------------------------------------------------------
/web/templates/homepage_detail.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | | Name |
5 | Price |
6 | Description |
7 | Type |
8 | Quantity |
9 |
10 |
11 |
12 | {{#.}}
13 |
14 |
15 | | {{name}} |
16 | {{price}} |
17 | {{description}} |
18 | {{type}} |
19 | {{quantity}} |
20 |
21 |
22 | {{/.}}
23 |
--------------------------------------------------------------------------------
/web/test/spec/CreateAccountViewSpec.js:
--------------------------------------------------------------------------------
1 | define([
2 | 'jquery',
3 | 'js/CreateAccountView',
4 | 'jasmine-jquery'
5 | ], function($, CreateAccountView) {
6 | var view;
7 |
8 | beforeEach(function() {
9 | view = new CreateAccountView();
10 | });
11 |
12 | describe("when view is constructing", function() {
13 | it("should return user name", function() {
14 | expect(view).toBeDefined();
15 | });
16 | });
17 | describe("should have value", function() {
18 | it('should return the value of #content', function () {
19 | expect(view.$el.find('#content')).toHaveValue();
20 | });
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/server/mapper/db_prototype.js:
--------------------------------------------------------------------------------
1 | var sqlite3 = require('sqlite3').verbose();
2 | var _ = require('underscore');
3 |
4 | function DBPrototype() {
5 | 'use strict';
6 | return;
7 | }
8 |
9 | DBPrototype.prototype.errorHandler = function (err) {
10 | 'use strict';
11 | if (err !== null) {
12 | throw err;
13 | }
14 | };
15 |
16 | DBPrototype.prototype.basic = function(sql, db_callback){
17 | 'use strict';
18 | var db = new sqlite3.Database("dev.db");
19 | db.all(sql, function (err, rows) {
20 | DBPrototype.prototype.errorHandler(err);
21 | db.close();
22 | db_callback(rows);
23 | });
24 | };
25 |
26 | module.exports = DBPrototype;
--------------------------------------------------------------------------------
/web/main.js:
--------------------------------------------------------------------------------
1 | require.config({
2 | baseUrl: '/',
3 | paths: {
4 | 'text': 'lib/text/text',
5 | jquery: 'lib/jquery/dist/jquery.min',
6 | json: 'lib/require/json',
7 | router: 'router',
8 | underscore: 'lib/underscore/underscore',
9 | mustache: 'lib/mustache/mustache',
10 | backbone: 'lib/backbone/backbone',
11 | "jquery-cookie": "lib/jquery.cookie/jquery.cookie",
12 | "config": 'config'
13 | },
14 | shim: {
15 | "jquery-cookie": ["jquery"],
16 | underscore: {
17 | exports: '_'
18 | }
19 | }
20 | });
21 |
22 | require(['app'], function(App){
23 | "use strict";
24 | App.initialize();
25 | });
--------------------------------------------------------------------------------
/web/test/spec/routerSpec.js:
--------------------------------------------------------------------------------
1 | //define([
2 | // 'sinon',
3 | // 'router',
4 | // 'jasmine-jquery'
5 | //], function(sinon, AppRouter) {
6 | // 'use strict';
7 | //
8 | // beforeEach(system() {
9 | // //this.router = new AppRouter();
10 | // //this.routeSpy = sinon.spy();
11 | // });
12 | //
13 | // describe("Router Test", system() {
14 | // it("should request the url and fetch", system () {
15 | // //this.router.bind("route:index", this.routeSpy);
16 | // //this.router.navigate("", true);
17 | // //expect(this.routeSpy).toHaveBeenCalledOnce();
18 | // //expect(this.routeSpy).toHaveBeenCalledWith();
19 | // });
20 | // });
21 | //});
22 |
--------------------------------------------------------------------------------
/server/mapper/db_helper.js:
--------------------------------------------------------------------------------
1 | var _ = require('underscore');
2 |
3 | function db_helper() {
4 | 'use strict';
5 | return;
6 | }
7 |
8 | db_helper.prototype.getValue = function (account) {
9 | 'use strict';
10 | var str = "";
11 | _.each(account, function (key) {
12 | str += "'" + key + "',";
13 | });
14 | str = str.substring(0, str.length - 1);
15 | return str;
16 | };
17 |
18 | db_helper.prototype.getKey = function (account) {
19 | 'use strict';
20 | var str = "";
21 | _.each(account, function (key, value) {
22 | str += value + ",";
23 | });
24 | str = str.substring(0, str.length - 1);
25 | return str;
26 | };
27 |
28 | module.exports = db_helper;
--------------------------------------------------------------------------------
/web/test/spec/LoginViewSpec.js:
--------------------------------------------------------------------------------
1 | define([
2 | 'jquery',
3 | 'js/LoginView',
4 | 'jasmine-jquery'
5 | ], function($, LoginView) {
6 | var view;
7 |
8 | beforeEach(function() {
9 | view = new LoginView();
10 | view.$el.find('#username').val('admin').trigger('change');
11 | view.$el.find('#password').val('admin').trigger('change');
12 | });
13 |
14 | describe("when view is constructing", function() {
15 | it("should return user name", function() {
16 | expect(view).toBeDefined();
17 | view.$el.find('#login').trigger('click');
18 | });
19 | });
20 | describe("should have value", function() {
21 | it('should return the value of #content', function () {
22 | expect(view.$el.find('#content')).toHaveValue();
23 | });
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/web/js/Model/UserModel.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | define(['backbone', "config"], function(Backbone, config) {
4 | var User = Backbone.Model.extend({
5 | initialize : function(username) {
6 | this.username = username;
7 | },
8 | defaults:{
9 | username:null
10 | }
11 | });
12 |
13 | var UserModel = Backbone.Collection.extend({
14 | default: {
15 | username:null
16 | },
17 | initialize : function(models, options) {
18 | this.user = new User(this.get('username'));
19 | this.username = options.username;
20 | },
21 | model: User,
22 | urlRoot: config.localhost + '/account/name/',
23 | "url": function () {
24 | return this.urlRoot + this.username;
25 | }
26 | });
27 |
28 | return UserModel;
29 | });
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "freerice",
3 | "version": "0.0.2",
4 | "homepage": "https://github.com/phodal/freerice",
5 | "authors": [
6 | "Phodal HUANG "
7 | ],
8 | "description": "free rice",
9 | "main": "web/main.js",
10 | "keywords": [
11 | "cms",
12 | "mobile",
13 | "cms",
14 | "free",
15 | "rice"
16 | ],
17 | "dependencies": {
18 | "backbone" : "1.1.2",
19 | "jquery": "2.1.1",
20 | "jquery.cookie": "1.4.1",
21 | "mustache": "0.8.2",
22 | "requirejs": "2.1.14",
23 | "underscore": "1.6.0",
24 | "text": "2.0.12",
25 | "sinon": "1.11.1",
26 | "jasmine-jquery": "2.0.5",
27 | "pure": "0.5.0"
28 | },
29 | "license": "MIT",
30 | "ignore": [
31 | "**/.*",
32 | "node_modules",
33 | "bower_components",
34 | ".*",
35 | "CHANGELOG.md",
36 | "component.json",
37 | "package.json",
38 | "_includes",
39 | "_layouts"
40 | ]
41 | }
42 |
--------------------------------------------------------------------------------
/server/service/account_service.js:
--------------------------------------------------------------------------------
1 | var AccountMapper = require("./../mapper/account_mapper");
2 | var db = new AccountMapper();
3 |
4 | function DBService() {
5 | 'use strict';
6 | return;
7 | }
8 |
9 | DBService.prototype.getAccountById = function (req, res, next) {
10 | 'use strict';
11 | var userId = req.params.id;
12 | db.getAccountById(userId, function (result) {
13 | res.send(result);
14 | next();
15 | });
16 | };
17 |
18 | DBService.prototype.getAccountByName = function (req, res, next) {
19 | 'use strict';
20 | var userName = req.params.name;
21 | db.getAccountByName(userName, function (result) {
22 | res.send(result);
23 | next();
24 | });
25 | };
26 |
27 | DBService.prototype.findAllAccount = function (req, res, next) {
28 | 'use strict';
29 | db.findAllAccount(function (result) {
30 | res.send(result);
31 | next();
32 | });
33 | };
34 |
35 | module.exports = DBService;
--------------------------------------------------------------------------------
/server/service/service_helper.js:
--------------------------------------------------------------------------------
1 | var bcrypt = require('bcrypt');
2 | var _ = require('underscore');
3 |
4 | function ServiceHelper() {
5 | 'use strict';
6 | return;
7 | }
8 |
9 | ServiceHelper.prototype.verifyRiceInput = function (rice) {
10 | 'use strict';
11 | return rice.name === undefined || rice.type === undefined || rice.price === undefined || rice.quantity === undefined ||rice.description === undefined;
12 | };
13 |
14 | ServiceHelper.prototype.verifyAccountInput = function (account) {
15 | 'use strict';
16 | return account.name === undefined || account.password === undefined || account.email === undefined;
17 | };
18 |
19 | ServiceHelper.prototype.encryptPassword = function encryptPassword(password, cb) {
20 | 'use strict';
21 | bcrypt.genSalt(10, function (err, salt) {
22 | bcrypt.hash(password, salt, function(err, hash) {
23 | cb(null, hash);
24 | });
25 | });
26 | };
27 |
28 | module.exports = ServiceHelper;
--------------------------------------------------------------------------------
/web/js/lib/Rice.js:
--------------------------------------------------------------------------------
1 | define([
2 | 'jquery',
3 | 'underscore',
4 | 'mustache',
5 | 'js/lib/UserSession',
6 | '../Model/CreateRiceModel'
7 | ],function($, _, Mustache, UserSession, CreateRice){
8 | 'use strict';
9 | function Rice(){
10 |
11 | }
12 | Rice.prototype.create = function(riceObject) {
13 | var createRice = new CreateRice({
14 | name: riceObject.name,
15 | type: riceObject.type,
16 | price: riceObject.price,
17 | quantity: riceObject.quantity,
18 | description: riceObject.description
19 | });
20 |
21 | createRice.save({}, {
22 | success: function(model, response) {
23 | if(response.status === "success"){
24 | console.log("createRice success");
25 | } else {
26 |
27 | }
28 | },
29 | error: function(model, response) {
30 | }
31 | });
32 | };
33 |
34 | return Rice;
35 | });
--------------------------------------------------------------------------------
/web/test/spec/UserSessionSpec.js:
--------------------------------------------------------------------------------
1 | define([
2 | 'js/lib/UserSession'
3 | ], function(UserSession) {
4 | 'use strict';
5 |
6 | describe("User Session", function() {
7 | it("should return default name,accessToken be null", function() {
8 | expect(UserSession.defaults.userName).toBe(null);
9 | expect(UserSession.defaults.accessToken).toBe(null);
10 | });
11 |
12 | it("should be authenticated after save session", function() {
13 | UserSession.save({
14 | name: "test",
15 | accessToken: "test"
16 | });
17 | expect(UserSession.authenticated()).toBe(true);
18 | });
19 |
20 | it("should can load session after save session", function() {
21 | UserSession.save({
22 | name: "test",
23 | accessToken: "test"
24 | });
25 | spyOn(UserSession, 'load');
26 | UserSession.initialize();
27 | expect(UserSession.load).toHaveBeenCalled();
28 | });
29 | });
30 | });
31 |
--------------------------------------------------------------------------------
/web/templates/create.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://travis-ci.org/phodal/freerice)
2 | [](https://codeclimate.com/github/phodal/freerice)
3 | [](https://codeclimate.com/github/phodal/freerice)
4 | [](https://david-dm.org/phodal/freerice.svg?style=flat0)
5 |
6 | #Moqi Mobiel CMS (Version: freerice)
7 |
8 | ##Front-end DEPENDENCIES
9 | - Backbone
10 | - RequireJS
11 | - Underscore
12 | - Mustache
13 | - Pure CSS
14 |
15 | ##Back-end DEPENDENCIES
16 |
17 | - RESTify
18 |
19 | ##Test DEPENDENCIES
20 |
21 | - Jasmine
22 | - Chai
23 | - Sinon
24 | - Mocha
25 | - Jasmine-jquery
26 |
27 | ##Quick Start
28 |
29 | 1. Run ``npm install``
30 | 2. Run ``db-migrate up``
31 | 3. Run ``node server/app.js`` (only:production)
32 |
33 | ## License
34 |
35 | © 2014 [Phodal Huang](http://www.phodal.com). This code is distributed under the MIT license.
36 |
37 |
--------------------------------------------------------------------------------
/web/js/CreateAccountView.js:
--------------------------------------------------------------------------------
1 | define([
2 | 'jquery',
3 | 'backbone',
4 | 'underscore',
5 | 'mustache',
6 | 'text!/templates/create.html',
7 | 'js/lib/User'
8 | ],function($, Backbone, _, Mustache, createAccountTemplate, User){
9 | 'use strict';
10 | var user = new User();
11 |
12 | var LoginView = Backbone.View.extend ({
13 | el: $("#content"),
14 |
15 | initialize: function(){
16 | },
17 | events: {
18 | "click #createAccount": "create_account"
19 | },
20 | create_account: function(event){
21 | event.preventDefault();
22 | var userInfo = {
23 | name: $('#fld_name').val(),
24 | password: $('#fld_password').val(),
25 | email: $('#fld_email').val()
26 | };
27 | user.create(userInfo);
28 | window.app.navigate('login', true);
29 | },
30 | render: function(){
31 | this.$el.html(Mustache.to_html(createAccountTemplate, {data:"data"}));
32 | }
33 | });
34 |
35 | return LoginView;
36 | });
--------------------------------------------------------------------------------
/web/js/HomeView.js:
--------------------------------------------------------------------------------
1 | define([
2 | 'jquery',
3 | 'backbone',
4 | 'underscore',
5 | 'mustache',
6 | 'text!/templates/homepage_detail.html',
7 | 'js/Model/RiceModel'
8 | ],function($, Backbone, _, Mustache, homepageTemplate, Rices){
9 | 'use strict';
10 | var HomeView = Backbone.View.extend ({
11 | el: $("#content"),
12 |
13 | initialize: function(){
14 | var that = this;
15 | this.collection = new Rices();
16 | this.collection.fetch({
17 | success: function(){
18 | that.render();
19 | }
20 | });
21 | },
22 | render: function(){
23 | this.$el.find("#content").remove();
24 | var result = [];
25 | _.each(this.collection.models, function(model){
26 | if(model.attributes.quantity !== 0 ){
27 | result.push(model.attributes);
28 | }
29 | });
30 | this.$el.html(Mustache.to_html(homepageTemplate, result));
31 | }
32 | });
33 |
34 | return HomeView;
35 | });
--------------------------------------------------------------------------------
/server/mapper/rice_mapper.js:
--------------------------------------------------------------------------------
1 | var sqlite3 = require('sqlite3').verbose();
2 | var _ = require('underscore');
3 | var DBHelper = require('./db_helper');
4 | var db_helper = new DBHelper();
5 | var DBPrototype = require('./db_prototype');
6 |
7 | function RiceMapper() {
8 | 'use strict';
9 | return;
10 | }
11 |
12 | RiceMapper.prototype = new DBPrototype();
13 |
14 | RiceMapper.prototype.findAllRice = function (callback) {
15 | 'use strict';
16 | var sql = "SELECT * FROM rice";
17 | RiceMapper.prototype.basic(sql, callback);
18 | };
19 |
20 | RiceMapper.prototype.createRice = function (rice, callback) {
21 | 'use strict';
22 |
23 | var sql = "INSERT OR REPLACE INTO RICE (" + db_helper.getKey(rice) + ") VALUES (" + db_helper.getValue(rice) + ");";
24 |
25 | var db = new sqlite3.Database("dev.db");
26 | db.all(sql, function (err, rows) {
27 | RiceMapper.prototype.errorHandler(err);
28 | db.close();
29 | if(_.isEmpty(rows)){
30 | rows = {
31 | "status": "success"
32 | };
33 | }
34 | callback(rows);
35 | });
36 | };
37 |
38 | module.exports = RiceMapper;
--------------------------------------------------------------------------------
/web/js/lib/UserSession.js:
--------------------------------------------------------------------------------
1 | define([
2 | 'jquery',
3 | 'underscore',
4 | 'backbone',
5 | 'jquery-cookie'
6 | ],function($, _, Backbone){
7 | 'use strict';
8 | var UserSession = Backbone.Model.extend({
9 | defaults: {
10 | 'accessToken': null,
11 | "userName": null
12 | },
13 | initialize: function(){
14 | this.load();
15 | },
16 | authenticated: function(){
17 | return !_.isEmpty($.cookie('accessToken')) && $.cookie('accessToken')!== null ;
18 | },
19 | principal: function() {
20 | return $.cookie('name');
21 | },
22 | save: function(authHash){
23 | $.cookie('name', authHash.name, { expires: 0.5 });
24 | $.cookie('accessToken', authHash.accessToken, { expires: 0.5 });
25 | },
26 | remove: function(){
27 | $.removeCookie('name');
28 | $.removeCookie('accessToken');
29 | },
30 | load: function(){
31 | this.userName = $.cookie('name');
32 | this.accessToken = $.cookie('accessToken');
33 | }
34 | });
35 |
36 | return new UserSession();
37 | });
--------------------------------------------------------------------------------
/web/js/UserProfileView.js:
--------------------------------------------------------------------------------
1 | define([
2 | 'jquery',
3 | 'backbone',
4 | 'underscore',
5 | 'mustache',
6 | 'text!/templates/userProfile.html',
7 | 'js/Model/UserModel',
8 | 'js/lib/UserSession'
9 | ],function($, Backbone, _, Mustache, userProfileTemplate, UserModel, UserSession){
10 | 'use strict';
11 | var HomeView = Backbone.View.extend ({
12 | el: $("#content"),
13 |
14 | initialize: function(){
15 | var that = this;
16 | this.collection = new UserModel([], {username: UserSession.principal()});
17 | this.collection.fetch({
18 | success: function(){
19 | that.render();
20 | }
21 | });
22 | },
23 | render: function(){
24 | this.$el.find("#content").remove();
25 | var result = [];
26 | _.each(this.collection.models, function(model){
27 | if(model.username !== null ){
28 | result.push(model.attributes);
29 | }
30 | });
31 | this.$el.html(Mustache.to_html(userProfileTemplate, result));
32 | }
33 | });
34 |
35 | return HomeView;
36 | });
--------------------------------------------------------------------------------
/web/js/LoginView.js:
--------------------------------------------------------------------------------
1 | define([
2 | 'jquery',
3 | 'underscore',
4 | 'mustache',
5 | 'text!/templates/login.html',
6 | 'js/lib/User',
7 | 'js/lib/UserSession'
8 | ],function($, _, Mustache, loginTemplate, User, UserSession){
9 | 'use strict';
10 | var user = new User();
11 |
12 | var LoginView = Backbone.View.extend ({
13 | el: $("#content"),
14 |
15 | initialize: function(){
16 |
17 | },
18 | events: {
19 | "click #login": "login"
20 | },
21 | login: function(event){
22 | event.preventDefault();
23 | var userInfo = {
24 | name: $('#username').val(),
25 | password: $('#password').val()
26 | };
27 | user.login(userInfo, function(){
28 | UserSession.save({
29 | name: userInfo.name,
30 | accessToken: userInfo.name
31 | });
32 | window.app.navigate('userProfile', true);
33 | });
34 | },
35 | render: function(){
36 | this.$el.html(Mustache.to_html(loginTemplate, {data:"data"}));
37 | }
38 | });
39 |
40 | return LoginView;
41 | });
--------------------------------------------------------------------------------
/server/service/rice_service.js:
--------------------------------------------------------------------------------
1 | var RiceMapper = require("./../mapper/rice_mapper");
2 | var db = new RiceMapper();
3 | var _ = require("underscore");
4 | var restify = require("restify");
5 | var ServiceHelper = require("./service_helper");
6 | var serviceHelper = new ServiceHelper();
7 |
8 | function Rice() {
9 | 'use strict';
10 | return;
11 | }
12 |
13 | Rice.prototype.findAllRice = function (req, res, next) {
14 | 'use strict';
15 | db.findAllRice(function (result) {
16 | res.send(result);
17 | next();
18 | });
19 | };
20 |
21 | Rice.prototype.create = function (req, res, next) {
22 | 'use strict';
23 |
24 | var rice = req.params;
25 |
26 | if (serviceHelper.verifyRiceInput(rice)) {
27 | return next(new restify.InvalidArgumentError('String must be supplied'));
28 | }
29 |
30 | db.createRice(rice, function(result) {
31 | if (result.status === "success") {
32 | res.send({status: "success"});
33 | next();
34 | } else {
35 | result = _.extend(result, {status: "fail"});
36 | res.send(result);
37 | next();
38 | }
39 | });
40 | };
41 |
42 | module.exports = Rice;
--------------------------------------------------------------------------------
/web/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Free Rice CMS
8 |
9 |
10 |
11 |
12 |
13 |
14 |
24 |
25 |
26 |
27 |
28 |
33 |
34 |
--------------------------------------------------------------------------------
/web/test/spec/RiceModelSpec.js:
--------------------------------------------------------------------------------
1 | define([
2 | 'sinon',
3 | 'js/Model/RiceModel'
4 | ], function(sinon, Rices) {
5 | 'use strict';
6 | var data = {"id":1,"name":"Rice","type":"Good","price":12,"quantity":1,"description":"Made in China"};
7 |
8 | beforeEach(function() {
9 | this.server = sinon.fakeServer.create();
10 | this.rices = new Rices();
11 | this.server.respondWith(
12 | "GET",
13 | "http://0.0.0.0:8080/all/rice",
14 | [
15 | 200,
16 | {"Content-Type": "application/json"},
17 | JSON.stringify(data)
18 | ]
19 | );
20 | });
21 |
22 | afterEach(function() {
23 | this.server.restore();
24 | });
25 |
26 | describe("Collection Test", function() {
27 | it("should request the url and fetch", function () {
28 | this.rices.fetch();
29 | expect(this.server.requests.length)
30 | .toEqual(1);
31 | expect(this.server.requests[0].method)
32 | .toEqual("GET");
33 | expect(this.server.requests[0].url)
34 | .toEqual("http://0.0.0.0:8080/all/rice");
35 | });
36 |
37 | it("should get data from the url", function() {
38 | this.rices.fetch();
39 | this.server.respond();
40 | var result = JSON.parse(JSON.stringify(this.rices.models[0]));
41 | expect(result["id"])
42 | .toEqual(1);
43 | expect(result["price"])
44 | .toEqual(12);
45 | expect(result)
46 | .toEqual(data);
47 | });
48 | });
49 | });
50 |
--------------------------------------------------------------------------------
/test/services/rice_test.js:
--------------------------------------------------------------------------------
1 | var http = require('http');
2 | var restify = require('restify');
3 |
4 | var client = restify.createJsonClient({
5 | url: 'http://127.0.0.1:8080/',
6 | version: '*'
7 | });
8 |
9 | describe('Create Rice Test', function() {
10 | it('should return create rice success', function (done) {
11 | client.post('/rice/create', {
12 | name: "rice name",
13 | type: "rice type",
14 | price: 12,
15 | quantity: 12,
16 | description: "description"
17 | }, function (err, req, res, data) {
18 | if (err) {
19 | throw new Error(err);
20 | }
21 | else {
22 | if (data.status != "success") {
23 | throw new Error('create failed');
24 | }
25 | res.destroy();
26 | done();
27 | }
28 | });
29 | });
30 |
31 | it('should return String must be supplied when without type', function (done) {
32 | client.post('/rice/create', {
33 | name: "rice name",
34 | price: 12,
35 | quantity: 12,
36 | description: "description"
37 | }, function (err, req, res, data) {
38 | if (err.message === "String must be supplied") {
39 | res.destroy();
40 | done();
41 | }
42 | });
43 | });
44 | });
45 |
--------------------------------------------------------------------------------
/test/services/authenticate_test.js:
--------------------------------------------------------------------------------
1 | var http = require('http');
2 | var restify = require('restify');
3 |
4 | var client = restify.createJsonClient({
5 | url: 'http://127.0.0.1:8080/',
6 | version: '*'
7 | });
8 |
9 | describe('Authenticate Test', function() {
10 | it('should return login success', function(done) {
11 | client.post('/login/user', { name: 'admin', password: "admin" }, function(err, req, res, data) {
12 | if (err) {
13 | throw new Error(err);
14 | }
15 | else {
16 | if (data.status != "success") {
17 | throw new Error('login failed');
18 | }
19 | done();
20 | }
21 | });
22 | });
23 |
24 | it('should return login failed', function(done) {
25 | client.post('/login/user', { name: 'admin', password: "noadmin" }, function(err, req, res, data) {
26 | if (err) {
27 | throw new Error(err);
28 | }
29 | else {
30 | if (data.status == "fail") {
31 | done();
32 | }
33 | }
34 | });
35 | });
36 |
37 | it('should return login failed when lost user name', function(done) {
38 | client.post('/login/user', { password: "noadmin" }, function(err, req, res, data) {
39 | if (err.message = "Name must be supplied") {
40 | done();
41 | }
42 | });
43 | });
44 |
45 | });
--------------------------------------------------------------------------------
/web/lib/require/async.js:
--------------------------------------------------------------------------------
1 | /** @license
2 | * RequireJS plugin for async dependency load like JSONP and Google Maps
3 | * Author: Miller Medeiros
4 | * Version: 0.1.1 (2011/11/17)
5 | * Released under the MIT license
6 | */
7 | define(function(){
8 |
9 | var DEFAULT_PARAM_NAME = 'callback',
10 | _uid = 0;
11 |
12 | function injectScript(src){
13 | var s, t;
14 | s = document.createElement('script'); s.type = 'text/javascript'; s.async = true; s.src = src;
15 | t = document.getElementsByTagName('script')[0]; t.parentNode.insertBefore(s,t);
16 | }
17 |
18 | function formatUrl(name, id){
19 | var paramRegex = /!(.+)/,
20 | url = name.replace(paramRegex, ''),
21 | param = (paramRegex.test(name))? name.replace(/.+!/, '') : DEFAULT_PARAM_NAME;
22 | url += (url.indexOf('?') < 0)? '?' : '&';
23 | return url + param +'='+ id;
24 | }
25 |
26 | function uid() {
27 | _uid += 1;
28 | return '__async_req_'+ _uid +'__';
29 | }
30 |
31 | return{
32 | load : function(name, req, onLoad, config){
33 | if(config.isBuild){
34 | onLoad(null); //avoid errors on the optimizer
35 | }else{
36 | var id = uid();
37 | //create a global variable that stores onLoad so callback
38 | //function can define new module after async load
39 | window[id] = onLoad;
40 | injectScript(formatUrl(name, id));
41 | }
42 | }
43 | };
44 | });
45 |
--------------------------------------------------------------------------------
/test/mapper/db_mapper_test.js:
--------------------------------------------------------------------------------
1 | var AccountMapper =require("../../server/mapper/account_mapper");
2 | var sqlite = new AccountMapper();
3 |
4 | var RiceMapper =require("../../server/mapper/rice_mapper");
5 | var riceDB = new RiceMapper();
6 |
7 |
8 | describe('DB Mapper Throw Error Test', function() {
9 | it('should throw error on errorHandler', function () {
10 | expect(sqlite.errorHandler()).to.throw();
11 | });
12 | });
13 |
14 | describe("Mapper Test", function () {
15 | describe('Create Account', function() {
16 | var account = { name: "user", password: "user", email: "newuser@phodal.com" };
17 |
18 | it('should return user exist when create account again', function(done) {
19 | sqlite.createAccount(account , function(){
20 | sqlite.createAccount(account , function(result){
21 | if(result.error === "user exist"){
22 | done();
23 | }
24 | });
25 | });
26 | });
27 | });
28 |
29 | describe('Create Rice', function() {
30 | it('should return the create rice', function(done) {
31 | var rice = { name: "zero", type: "user", quantity: 23, price:23, description: "newuser@phodal.com" };
32 |
33 | riceDB.createRice(rice , function(result){});
34 | riceDB.findAllRice(function(result){
35 | if(result[0].description = "newuser@phodal.com"){
36 | done();
37 | }
38 | });
39 | });
40 | });
41 |
42 | });
--------------------------------------------------------------------------------
/server/app.js:
--------------------------------------------------------------------------------
1 | var restify = require('restify');
2 | var server = restify.createServer();
3 |
4 | var Authenticate = require('./service/authenticate');
5 | var auth = new Authenticate();
6 |
7 | var DBService = require('./service/account_service');
8 | var get_response = new DBService();
9 |
10 | var Rice = require('./service/rice_service');
11 | var rice = new Rice();
12 |
13 | server.use(restify.gzipResponse());
14 | server.use(restify.bodyParser());
15 | server.use(restify.acceptParser(['json', 'application/json']));
16 | server.use(
17 | function crossOrigin(req,res,next){
18 | 'use strict';
19 | res.header("Access-Control-Allow-Origin", "*");
20 | res.header("Access-Control-Allow-Headers", "X-Requested-With");
21 | return next();
22 | }
23 | );
24 |
25 | server.get('/all/account', get_response.findAllAccount);
26 | server.get('/account/id/:id', get_response.getAccountById);
27 | server.get('/account/name/:name', get_response.getAccountByName);
28 |
29 | server.post('/account/create', auth.create);
30 | server.post('/login/user', auth.login);
31 |
32 | server.get('/all/rice', rice.findAllRice);
33 | server.post('/rice/create', rice.create);
34 |
35 | server.get('/', restify.serveStatic({
36 | directory: 'web',
37 | default: 'index.html'
38 | }));
39 |
40 | server.get(/\/?.*/, restify.serveStatic({
41 | directory: 'web'
42 | }));
43 |
44 |
45 | server.listen(8080, function () {
46 | 'use strict';
47 | console.log('%s listening at %s', server.name, server.url);
48 | });
49 |
--------------------------------------------------------------------------------
/Gruntfile.js:
--------------------------------------------------------------------------------
1 | module.exports = function (grunt) {
2 | "use strict";
3 |
4 | grunt.initConfig({
5 |
6 | pkg: grunt.file.readJSON('package.json')
7 | , jasmine: {
8 | src: "web/test/lib/*.js"
9 | , options: {
10 | specs: "web/test/spec/*Spec.js",
11 | template: require('grunt-template-jasmine-requirejs'),
12 | templateOptions:{
13 | requireConfig: {
14 | baseUrl: './web',
15 | paths: {
16 | 'text': 'lib/text/text',
17 | jquery: 'lib/jquery/dist/jquery.min',
18 | json: 'lib/require/json',
19 | router: 'router',
20 | underscore: 'lib/underscore/underscore',
21 | mustache: 'lib/mustache/mustache',
22 | backbone: 'lib/backbone/backbone',
23 | "jquery-cookie": "lib/jquery.cookie/jquery.cookie",
24 | "jasmine-jquery": "lib/jasmine-jquery/lib/jasmine-jquery",
25 | sinon: "lib/sinon/lib/sinon"
26 | },
27 | shim: {
28 | "jquery-cookie": ["jquery"],
29 | backbone: {
30 | exports:"Backbone"
31 | },
32 | underscore: {
33 | exports: '_'
34 | }
35 | }
36 | }
37 | }
38 | }
39 | }
40 | });
41 |
42 | grunt.loadNpmTasks('grunt-contrib-jasmine');
43 | grunt.registerTask('server', 'Start a custom web server.', function() {
44 | grunt.log.writeln('Starting web server on port 8080.');
45 | require('./server/app.js');
46 | });
47 | grunt.registerTask('default', ['server','jasmine']);
48 | };
49 |
--------------------------------------------------------------------------------
/web/templates/managePage.html:
--------------------------------------------------------------------------------
1 |
37 |
38 |
39 |
40 |
41 | | Name |
42 | Price |
43 | Description |
44 | Type |
45 | Quantity |
46 |
47 |
48 |
49 | {{#.}}
50 |
51 |
52 | | {{name}} |
53 | {{price}} |
54 | {{description}} |
55 | {{type}} |
56 | {{quantity}} |
57 |
58 |
59 | {{/.}}
60 |
--------------------------------------------------------------------------------
/web/js/lib/User.js:
--------------------------------------------------------------------------------
1 | define([
2 | 'jquery',
3 | 'underscore',
4 | 'mustache',
5 | 'js/lib/UserSession',
6 | '../Model/CreateAccountModel',
7 | '../Model/UserLogin'
8 | ],function($, _, Mustache, UserSession, CreateAccount, LoginAccount){
9 | 'use strict';
10 | function User(){
11 |
12 | }
13 | User.prototype.login = function(userObject, callback) {
14 | var login = new LoginAccount({
15 | name: userObject.name,
16 | password: userObject.password
17 | });
18 |
19 | login.save({}, {
20 | success: function(model, response) {
21 | if(response.status === "success"){
22 | var name = userObject.name;
23 | UserSession.save({
24 | "name": name,
25 | "accessToken": response.accessToken
26 | });
27 | callback();
28 | } else {
29 | }
30 | },
31 | error: function(model, response) {
32 | callback(model, response);
33 | }
34 | });
35 | };
36 |
37 | User.prototype.create = function(userObject) {
38 | var login = new CreateAccount({
39 | name: userObject.name,
40 | email: userObject.email,
41 | password: userObject.password
42 | });
43 |
44 | login.save({}, {
45 | success: function(model, response) {
46 | if(response.status === "success"){
47 | console.log("login success");
48 | } else {
49 |
50 | }
51 | },
52 | error: function(model, response) {
53 | }
54 | });
55 | };
56 |
57 | return User;
58 | });
--------------------------------------------------------------------------------
/web/js/AdminView.js:
--------------------------------------------------------------------------------
1 | define([
2 | 'jquery',
3 | 'backbone',
4 | 'underscore',
5 | 'mustache',
6 | 'text!/templates/managePage.html',
7 | 'js/lib/Rice',
8 | 'js/Model/RiceModel'
9 | ],function($, Backbone, _, Mustache, manageTemplate, Rice, RiceModel){
10 | 'use strict';
11 | var rice = new Rice();
12 |
13 | var AdminView = Backbone.View.extend ({
14 | el: $("#content"),
15 |
16 | initialize: function(){
17 | var that = this;
18 | this.collection = new RiceModel();
19 | this.collection.fetch({
20 | success: function(){
21 | that.render();
22 | }
23 | });
24 | },
25 | events: {
26 | "click #createRice": "create_rice"
27 | },
28 | create_rice: function(event){
29 | event.preventDefault();
30 | var riceInfo = {
31 | name: $('input[name="name"]').val(),
32 | type: $('input[name="type"]').val(),
33 | price: $('input[name="price"]').val(),
34 | quantity: $('input[name="quantity"]').val(),
35 | description: $('input[name="description"]').val()
36 | };
37 | rice.create(riceInfo);
38 | window.app.navigate('admin', true);
39 | },
40 | render: function(){
41 | this.$el.find("#content").remove();
42 | var result = [];
43 | _.each(this.collection.models, function(model){
44 | if(model.attributes.quantity !== 0 ){
45 | result.push(model.attributes);
46 | }
47 | });
48 | this.$el.html(Mustache.to_html(manageTemplate, result));
49 | }
50 | });
51 |
52 | return AdminView;
53 | });
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "freerice",
3 | "version": "0.2.0",
4 | "description": "MoQi Mobile CMS.",
5 | "repository": {
6 | "type": "git",
7 | "url": "git@github.com:phodal/freerice.git"
8 | },
9 | "keywords": [
10 | "mobile cms",
11 | "cms",
12 | "mobile"
13 | ],
14 | "dependencies": {
15 | "underscore": "1.7.0",
16 | "restify": "2.8.3",
17 | "sqlite3": "3.0.2",
18 | "db-migrate": "0.7.1",
19 | "bcrypt": "~0.8.0",
20 | "bower": "1.3.12"
21 | },
22 | "author": [
23 | {
24 | "name": "Fengda HUANG",
25 | "url": "http://www.phodal.com/"
26 | }
27 | ],
28 | "license": "MIT",
29 | "devDependencies": {
30 | "bl": "~0.9.0",
31 | "chai": "~1.9.1",
32 | "codeclimate-test-reporter": "0.0.4",
33 | "istanbul": "0.3.2",
34 | "jslint": "0.6.4",
35 | "grunt": "0.4.5",
36 | "grunt-cli": "0.1.13",
37 | "grunt-contrib-connect":"0.8.0",
38 | "grunt-contrib-jasmine": "0.8.1",
39 | "grunt-template-jasmine-requirejs": "0.2.0",
40 | "grunt-exec": "0.4.5",
41 | "mocha": "~2.0.1",
42 | "pre-commit": "0.0.9",
43 | "request": "2.45.0",
44 | "requirejs": "^2.1.15",
45 | "sinon": "~1.11.1",
46 | "zombie": "2.0.1"
47 | },
48 | "pre-commit": [
49 | "jslint",
50 | "test"
51 | ],
52 | "scripts": {
53 | "start": "node server/app.js",
54 | "initdb": "rm dev.db;./node_modules/.bin/db-migrate up",
55 | "install": "bower install",
56 | "test": "grunt jasmine;rm dev.db;./node_modules/.bin/db-migrate up;istanbul cover node_modules/mocha/bin/_mocha -- -R spec 'test/**/*_test.js';",
57 | "jslint": "jslint --edition=latest 'server/*.js' 'server/mapper/*.js' 'server/service/*.js' web/js/**/*.js web/js/*.js web/*.js",
58 | "sendCoverage": "CODECLIMATE_REPO_TOKEN=62ad5b3cbab8aee9e9c32763f7097a0eb673cd2705442d4049b5c9464bb09850 codeclimate < coverage/lcov.info"
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/server/service/authenticate.js:
--------------------------------------------------------------------------------
1 | var AccountMapper = require("./../mapper/account_mapper");
2 | var _ = require("underscore");
3 | var restify = require("restify");
4 | var bcrypt = require('bcrypt');
5 | var ServiceHelper = require("./service_helper");
6 |
7 | var db = new AccountMapper();
8 | var serviceHelper = new ServiceHelper();
9 |
10 | function Authenticate() {
11 | 'use strict';
12 | return;
13 | }
14 |
15 | Authenticate.prototype.login = function (req, res, next) {
16 | 'use strict';
17 | var account = req.params;
18 |
19 | if (account.name === undefined || account.password === undefined) {
20 | return next(new restify.InvalidArgumentError('Name must be supplied'));
21 | }
22 |
23 | db.getPasswordByName(account.name, function (result) {
24 | bcrypt.compare(account.password, _.first(result).password, function(err, success) {
25 | if (success) {
26 | res.send({status: "success"});
27 | } else {
28 | res.send({status: "fail"});
29 | }
30 | next();
31 | });
32 | });
33 | };
34 |
35 | Authenticate.prototype.create = function (req, res, next) {
36 | 'use strict';
37 |
38 | var account = req.params;
39 | if (serviceHelper.verifyAccountInput(account)) {
40 | return next(new restify.InvalidArgumentError('Name must be supplied'));
41 | }
42 |
43 | serviceHelper.encryptPassword(account.password, function (err, password) {
44 | if (err) {
45 | throw new Error(err);
46 | }
47 | account.password = password;
48 | db.createAccount(account, function (result) {
49 | if (result.status === "success") {
50 | res.send({status: "success"});
51 | } else {
52 | result = _.extend(result, {status: "fail"});
53 | res.send(result);
54 | }
55 | next();
56 | });
57 | });
58 | };
59 |
60 |
61 | module.exports = Authenticate;
--------------------------------------------------------------------------------
/test/rest/rest_test.js:
--------------------------------------------------------------------------------
1 | var http = require('http');
2 | var bl = require('bl');
3 |
4 | describe('RESTful GET Test', function () {
5 | before(function() {
6 |
7 | });
8 |
9 | it('should return 200 when start rest server', function (done) {
10 | http.get('http://localhost:8080/', function (res) {
11 | assert(200, res.statusCode);
12 | done();
13 | });
14 | });
15 |
16 | it('should return at least one account on /all/account', function (done) {
17 | http.get('http://localhost:8080/all/account', function (res) {
18 | res.pipe(bl(function(err, data) {
19 | var json = JSON.parse(data)[0];
20 | if(json.id === 1){
21 | done();
22 | }
23 | }));
24 | });
25 | });
26 |
27 | it('should return at least one account on /all/rice', function (done) {
28 | http.get('http://localhost:8080/all/rice', function (res) {
29 | res.pipe(bl(function(err, data) {
30 | var json = JSON.parse(data)[0];
31 | if(json.id === 1){
32 | done();
33 | }
34 | }));
35 | });
36 | });
37 |
38 | it('should return the AdminCat when get by id = 1', function (done) {
39 | http.get('http://localhost:8080/account/id/1', function (res) {
40 | res.pipe(bl(function(err, data) {
41 | var json = JSON.parse(data)[0];
42 | if(json.id === 1){
43 | done();
44 | }
45 | }));
46 | });
47 | });
48 |
49 | it('should return the AdminCat by account_name', function (done) {
50 | http.get('http://localhost:8080/account/name/admin', function (res) {
51 | res.pipe(bl(function(err, data) {
52 | var json = JSON.parse(data)[0];
53 | if(json.name === 'admin'){
54 | done();
55 | }
56 | }));
57 | });
58 | });
59 | });
--------------------------------------------------------------------------------
/server/mapper/account_mapper.js:
--------------------------------------------------------------------------------
1 | var sqlite3 = require('sqlite3').verbose();
2 | var _ = require('underscore');
3 | var DBHelper = require('./db_helper');
4 | var db_helper = new DBHelper();
5 | var DBPrototype = require('./db_prototype');
6 |
7 | function AccountMapper() {
8 | 'use strict';
9 | return;
10 | }
11 |
12 | AccountMapper.prototype = new DBPrototype();
13 |
14 | AccountMapper.prototype.getAccountById = function (user_id, callback) {
15 | 'use strict';
16 | var sql = "SELECT id,name,email FROM user WHERE id = " + user_id;
17 | AccountMapper.prototype.basic(sql, callback);
18 | };
19 |
20 | AccountMapper.prototype.getPasswordByName = function (user_name, callback) {
21 | 'use strict';
22 | var sql = "SELECT * FROM user WHERE name = '" + user_name + "' LIMIT 1";
23 | AccountMapper.prototype.basic(sql, callback);
24 | };
25 |
26 | AccountMapper.prototype.getAccountByName = function (user_name, callback) {
27 | 'use strict';
28 | var sql = "SELECT id,name,email FROM user WHERE name = '" + user_name + "' LIMIT 1";
29 | AccountMapper.prototype.basic(sql, callback);
30 | };
31 |
32 | AccountMapper.prototype.findAllAccount = function (callback) {
33 | 'use strict';
34 | var sql = "SELECT id,name,email FROM user";
35 | AccountMapper.prototype.basic(sql, callback);
36 | };
37 |
38 | AccountMapper.prototype.createAccount = function (account, callback) {
39 | 'use strict';
40 |
41 | function createNewAccount() {
42 | var sql = "INSERT OR REPLACE INTO USER (" + db_helper.getKey(account) + ") VALUES (" + db_helper.getValue(account) + ");";
43 |
44 | var db = new sqlite3.Database("dev.db");
45 | db.all(sql, function (err, rows) {
46 | AccountMapper.prototype.errorHandler(err);
47 | db.close();
48 | if (_.isEmpty(rows)) {
49 | rows = {
50 | "status": "success"
51 | };
52 | } else {
53 | rows = {};
54 | }
55 | callback(rows);
56 | });
57 | }
58 |
59 | AccountMapper.prototype.getAccountByName(account.name, function(result){
60 | if(!_.isEmpty(result)){
61 | return callback({
62 | "error": "user exist"
63 | });
64 | }
65 | createNewAccount();
66 | });
67 | };
68 |
69 | module.exports = AccountMapper;
--------------------------------------------------------------------------------
/web/lib/require/json.js:
--------------------------------------------------------------------------------
1 | /** @license
2 | * RequireJS plugin for loading JSON files
3 | * - depends on Text plugin and it was HEAVILY "inspired" by it as well.
4 | * Author: Miller Medeiros
5 | * Version: 0.3.1 (2013/02/04)
6 | * Released under the MIT license
7 | */
8 | define(['text'], function(text){
9 |
10 | var CACHE_BUST_QUERY_PARAM = 'bust',
11 | CACHE_BUST_FLAG = '!bust',
12 | jsonParse = (typeof JSON !== 'undefined' && typeof JSON.parse === 'function')? JSON.parse : function(val){
13 | return eval('('+ val +')'); //quick and dirty
14 | },
15 | buildMap = {};
16 |
17 | function cacheBust(url){
18 | url = url.replace(CACHE_BUST_FLAG, '');
19 | url += (url.indexOf('?') < 0)? '?' : '&';
20 | return url + CACHE_BUST_QUERY_PARAM +'='+ Math.round(2147483647 * Math.random());
21 | }
22 |
23 | //API
24 | return {
25 |
26 | load : function(name, req, onLoad, config) {
27 | if ( config.isBuild && (config.inlineJSON === false || name.indexOf(CACHE_BUST_QUERY_PARAM +'=') !== -1) ) {
28 | //avoid inlining cache busted JSON or if inlineJSON:false
29 | onLoad(null);
30 | } else {
31 | text.get(req.toUrl(name), function(data){
32 | if (config.isBuild) {
33 | buildMap[name] = data;
34 | onLoad(data);
35 | } else {
36 | onLoad(jsonParse(data));
37 | }
38 | },
39 | onLoad.error, {
40 | accept: 'application/json'
41 | }
42 | );
43 | }
44 | },
45 |
46 | normalize : function (name, normalize) {
47 | //used normalize to avoid caching references to a "cache busted" request
48 | return (name.indexOf(CACHE_BUST_FLAG) === -1)? name : cacheBust(name);
49 | },
50 |
51 | //write method based on RequireJS official text plugin by James Burke
52 | //https://github.com/jrburke/requirejs/blob/master/text.js
53 | write : function(pluginName, moduleName, write){
54 | if(moduleName in buildMap){
55 | var content = buildMap[moduleName];
56 | write('define("'+ pluginName +'!'+ moduleName +'", function(){ return '+ content +';});\n');
57 | }
58 | }
59 |
60 | };
61 | });
62 |
--------------------------------------------------------------------------------
/web/router.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | define([
4 | 'jquery',
5 | 'underscore',
6 | 'backbone',
7 | 'js/HomeView.js',
8 | 'js/LoginView.js',
9 | 'js/CreateAccountView.js',
10 | 'js/lib/User.js',
11 | 'js/UserProfileView.js',
12 | 'js/lib/UserSession.js',
13 | 'js/lib/Logout.js',
14 | 'js/AdminView'
15 | ],function($, _, Backbone, HomeView, LoginView, CreateAccountView, User, UserProfileView, UserSession, Logout, AdminView){
16 | var AppRouter = Backbone.Router.extend({
17 | index: function(){
18 | var homeView = new HomeView();
19 | homeView.initialize();
20 | },
21 | createAccount: function(){
22 | var createAccountView = new CreateAccountView();
23 | createAccountView.render();
24 | },
25 | login: function(){
26 | var loginView = new LoginView();
27 | loginView.render();
28 | },
29 | logout: function(){
30 | var logout = new Logout();
31 | logout.logout();
32 | this.navigate('/', true);
33 | },
34 | admin: function(){
35 | if(UserSession.authenticated() !== true ){
36 | this.navigate('login', true);
37 | }
38 | var adminView = new AdminView();
39 | adminView.render();
40 | },
41 | userProfile: function(){
42 | if (UserSession.authenticated() === true) {
43 | var userProfileView = new UserProfileView();
44 | userProfileView.render();
45 | } else {
46 | this.navigate('account/login', true);
47 | Backbone.history.loadUrl();
48 | }
49 | },
50 | initialize: function() {
51 | var router = this,
52 | routes = [
53 | [ /^.*$/, "index" ],
54 | [ "account/create", "createAccount" ],
55 | [ "account/login", "login" ],
56 | [ "account/logout", "logout" ],
57 | [ "userProfile", "userProfile" ],
58 | [ "admin", "admin" ]
59 | ];
60 |
61 | _.each(routes, function(route) {
62 | router.route.apply(router,route);
63 | });
64 | Backbone.history.stop();
65 | Backbone.history.start();
66 | }
67 | });
68 |
69 | window.app = new AppRouter();
70 | return AppRouter;
71 | });
--------------------------------------------------------------------------------
/test/services/create_account_test.js:
--------------------------------------------------------------------------------
1 | var http = require('http');
2 | var restify = require('restify');
3 | var request = require('request');
4 | var _ = require('underscore');
5 |
6 | var client = restify.createJsonClient({
7 | url: 'http://127.0.0.1:8080/',
8 | version: '*'
9 | });
10 |
11 | var client2 = restify.createJsonClient({
12 | url: 'http://127.0.0.1:8080/',
13 | version: '*'
14 | });
15 |
16 | describe('Create Account Test', function() {
17 | it('should return create success', function (done) {
18 | var randomNumber = _.random(0, 100);
19 | client2.post('/account/create', {
20 | name: 'user' + randomNumber,
21 | password: "user" + randomNumber,
22 | email: "user@phodal.com"
23 | }, function (err, req, res, data) {
24 | if (err) {
25 | throw new Error(err);
26 | }
27 | else {
28 | if (data.status !== "success") {
29 | throw new Error('create failed');
30 | } else {
31 | res.destroy();
32 | done();
33 | }
34 | }
35 | });
36 | });
37 |
38 | it('should return Name must be supplied when without name', function (done) {
39 | client.post('/account/create', {password: "user", email: "user@phodal.com"}, function (err, req, res, data) {
40 | if (err.message === "Name must be supplied") {
41 | res.destroy();
42 | done();
43 | }
44 | });
45 | });
46 | });
47 |
48 | describe('Visit Account Test', function() {
49 | it('should return 404 when visit the /account/create', function (done) {
50 | request('http://127.0.0.1:8080/account/create', function (error, response, body) {
51 | if (response.statusCode === 404) {
52 | done();
53 | }
54 | });
55 | });
56 |
57 | it('should return 404 when visit the /account/create', function (done) {
58 | request('http://127.0.0.1:8080/account/create', function (error, response, body) {
59 | if (JSON.parse(body).code === "ResourceNotFound") {
60 | done();
61 | }
62 | });
63 | });
64 | });
65 |
66 | describe('Create Account Test', function() {
67 | it('should return Name must be supplied when name repeat', function(done) {
68 | request.post({url:'http://127.0.0.1:8080/account/create',
69 | form: { name: "admin", password: "user", email: "newuser@phodal.com" }},
70 | function(err, httpResponse, body){
71 | if (err) {
72 | throw new Error(err);
73 | }
74 | else {
75 | if (JSON.parse(body).error === "user exist") {
76 | done();
77 | }
78 | }
79 | });
80 | });
81 | });
--------------------------------------------------------------------------------
/test/system/function_test.js:
--------------------------------------------------------------------------------
1 | var Browser = require('zombie');
2 | var _ = require('underscore');
3 | var website = "http://0.0.0.0:8080/";
4 |
5 | describe("Function Test", function () {
6 | describe("Authenticate", function () {
7 | var browser = new Browser({ site: website });
8 |
9 | it("should load the home page", function(done) {
10 | browser.visit('/', function (error) {
11 | assert.ifError(error);
12 | browser.assert.success();
13 | });
14 | done();
15 | });
16 |
17 | it('should be on login page', function(done) {
18 | browser.visit('#/account/login')
19 | .then(function() {
20 | assert.equal(browser.location.href, website + '#/account/login', 'It is not the Login page');
21 | assert.ok(browser.success, 'It did not load successfully.');
22 | })
23 | .then(done, done);
24 | });
25 |
26 | it('should redirect to user profile after login', function(done) {
27 | browser.visit('#/account/login')
28 | .then(function() {
29 | browser.fill('input[name=username]', 'admin');
30 | browser.fill('input[name=password]', 'admin');
31 | browser.pressButton("Sign in", function() {
32 | if(browser.location.href === website + '#userProfile'){
33 | done();
34 | }
35 | });
36 | });
37 | });
38 |
39 | it('should redirect to homepage after logout', function(done) {
40 | browser.visit('#/account/logout')
41 | .then(function() {
42 | if(browser.location.href === website + '#'){
43 | done();
44 | }
45 | });
46 | });
47 | });
48 | describe("Create Account", function () {
49 | var browser = new Browser({ site: website });
50 | var randomNumber = _.random(1, 1000);
51 | var userName = 'user' + randomNumber;
52 |
53 | it("should redirect to login in page after create account", function () {
54 | browser.visit('#account/create')
55 | .then(function() {
56 | browser.fill('input[name=name]', userName);
57 | browser.fill('input[name=password]', userName);
58 | browser.fill('input[name=email]', userName + "@gmail.com");
59 | browser.pressButton("Sign in", function() {
60 | if(browser.location.href === website + '#/account/login'){
61 | done();
62 | }
63 | });
64 | });
65 | });
66 |
67 | it("should login with created user", function () {
68 | browser.visit('#/account/login')
69 | .then(function() {
70 | browser.fill('input[name=username]', userName);
71 | browser.fill('input[name=password]', userName);
72 | browser.pressButton("Sign in", function() {
73 | if(browser.location.href === website + '#userProfile'){
74 | done();
75 | }
76 | });
77 | });
78 | });
79 | });
80 | });
81 |
--------------------------------------------------------------------------------
/web/css/pricing.css:
--------------------------------------------------------------------------------
1 | /*
2 | * -- BASE STYLES --
3 | * Most of these are inherited from Base, but I want to change a few.
4 | */
5 | body {
6 | color: #526066;
7 | }
8 |
9 | h2, h3 {
10 | letter-spacing: 0.25em;
11 | text-transform: uppercase;
12 | font-weight: 600;
13 | }
14 |
15 |
16 | /*
17 | * -- Layout Styles --
18 | */
19 | .l-content {
20 | margin: 0 auto;
21 | }
22 |
23 | .l-box {
24 | padding: 0.5em 2em;
25 | }
26 |
27 | /*
28 | * -- MENU STYLES --
29 | * Make the menu have a very faint box-shadow.
30 | */
31 | .pure-menu {
32 | box-shadow: 0 1px 1px rgba(0,0,0, 0.10);
33 | }
34 |
35 |
36 | /*
37 | * -- BANNER --
38 | * The top banner with the headings. By using a combination
39 | * of `display: table;` and `display: table-cell;`, we can
40 | * vertically center the text.
41 | */
42 |
43 | .banner {
44 | background: transparent url('../images/banner.jpg') 0 0 no-repeat fixed;
45 | text-align: center;
46 | background-size: cover;
47 | filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='../images/banner.jpg', sizingMethod='scale');
48 |
49 | height: 200px;
50 | width: 100%;
51 | margin-bottom: 3em;
52 | display: table;
53 | }
54 |
55 | .banner-head {
56 | display: table-cell;
57 | vertical-align: middle;
58 | margin-bottom: 0;
59 | font-size: 2em;
60 | color: white;
61 | font-weight: 500;
62 | text-shadow: 0 1px 1px black;
63 | }
64 |
65 |
66 |
67 | /*
68 | * -- PRICING TABLE WRAPPER --
69 | * This element wraps up all the pricing table elements
70 | */
71 | .pricing-tables,
72 | .information {
73 | max-width: 980px;
74 | margin: 0 auto;
75 | }
76 | .pricing-tables {
77 | margin-bottom: 3.125em;
78 | text-align: center;
79 | }
80 |
81 | /*
82 | * -- PRICING TABLE --
83 | * Every pricing table has the .pricing-table class
84 | */
85 | .pricing-table {
86 | border: 1px solid #ddd;
87 | margin: 0 0.5em 2em;
88 | padding: 0 0 3em;
89 | }
90 |
91 | /*
92 | * -- PRICING TABLE HEADER COLORS --
93 | * Choose a different color based on the type of pricing table.
94 | */
95 | .pricing-table-free .pricing-table-header {
96 | background: #519251;
97 | }
98 |
99 | .pricing-table-biz .pricing-table-header {
100 | background: #2c4985;
101 | }
102 |
103 | /*
104 | * -- PRICING TABLE HEADER --
105 | * By default, a header is black/white, and has some styles for its name.
106 | */
107 | .pricing-table-header {
108 | background: #111;
109 | color: #fff;
110 | }
111 | .pricing-table-header h2 {
112 | margin: 0;
113 | padding-top: 2em;
114 | font-size: 1em;
115 | font-weight: normal;
116 |
117 | }
118 |
119 |
120 | /*
121 | * -- PRICING TABLE PRICE --
122 | * Styles for the price and the corresponding per month
123 | */
124 | .pricing-table-price {
125 | font-size: 6em;
126 | margin: 0.2em 0 0;
127 | font-weight: 100;
128 | }
129 | .pricing-table-price span {
130 | display: block;
131 | text-transform: uppercase;
132 | font-size: 0.2em;
133 | padding-bottom: 2em;
134 | font-weight: 400;
135 | color: rgba(255, 255, 255, 0.5);
136 | *color: #fff;
137 | }
138 |
139 |
140 |
141 | /*
142 | * -- PRICING TABLE LIST --
143 | * Each pricing table has a which is denoted by the .pricing-table-list class
144 | */
145 | .pricing-table-list {
146 | list-style-type: none;
147 | margin: 0;
148 | padding: 0;
149 | text-align: center;
150 | }
151 |
152 |
153 | /*
154 | * -- PRICING TABLE LIST ELEMENTS --
155 | * Styles for the individual list elements within each pricing table
156 | */
157 | .pricing-table-list li {
158 | padding: 0.8em 0;
159 | background: #f7f7f7;
160 | border-bottom: 1px solid #e7e7e7;
161 | }
162 |
163 |
164 | /*
165 | * -- PRICING TABLE BUTTON --
166 | * Styles for the "Choose" button at the bottom of a pricing table.
167 | * This inherits from Pure Button.
168 | */
169 | .button-choose {
170 | border: 1px solid #ccc;
171 | background: #fff;
172 | color: #333;
173 | border-radius: 2em;
174 | font-weight: bold;
175 | position: relative;
176 | bottom: -1.5em;
177 | }
178 |
179 | .information-head {
180 | color: black;
181 | font-weight: 500;
182 | }
183 |
184 | .footer {
185 | position:absolute; /* your requirement, you can also use fixed though */
186 | bottom:0;
187 | width:100%;
188 | background: #111;
189 | color: #888;
190 | text-align: center;
191 | }
192 | .footer a {
193 | color: #ddd;
194 | text-decoration: none;
195 | }
196 |
197 |
198 |
199 | /*
200 | * -- TABLET MEDIA QUERIES --
201 | * On tablets, we want to slightly adjust the size of the banner
202 | * text and add some vertical space between the various pricing tables
203 | */
204 | @media(min-width: 767px) {
205 |
206 | .banner-head {
207 | font-size: 4em;
208 | }
209 | .pricing-table {
210 | margin-bottom: 0;
211 | }
212 |
213 | }
214 |
215 | /*
216 | * -- PHONE MEDIA QUERIES --
217 | * On phones, we want to reduce the height and font-size of the banner further
218 | */
219 | @media (min-width: 480px) {
220 | .banner {
221 | height: 400px;
222 | }
223 | .banner-head {
224 | font-size: 3em;
225 | }
226 | }
227 |
228 | .sidr {
229 | display: none;
230 | position: absolute;
231 | position: fixed;
232 | top: 0;
233 | height: 100%;
234 | z-index: 999999;
235 | width: 260px;
236 | overflow-x: none;
237 | overflow-y: auto;
238 | font-family: "lucida grande", tahoma, verdana, arial, sans-serif;
239 | font-size: 15px;
240 | background: #f8f8f8;
241 | color: #333;
242 | -webkit-box-shadow: inset 0 0 5px 5px #ebebeb;
243 | -moz-box-shadow: inset 0 0 5px 5px #ebebeb;
244 | box-shadow: inset 0 0 5px 5px #ebebeb
245 | }
246 | .sidr .sidr-inner {
247 | padding: 0 0 15px
248 | }
249 | .sidr .sidr-inner>p {
250 | margin-left: 15px;
251 | margin-right: 15px
252 | }
253 | .sidr.right {
254 | left: auto;
255 | right: -260px
256 | }
257 | .sidr.left {
258 | left: -260px;
259 | right: auto
260 | }
261 | .sidr h1, .sidr h2, .sidr h3, .sidr h4, .sidr h5, .sidr h6 {
262 | font-size: 11px;
263 | font-weight: normal;
264 | padding: 0 15px;
265 | margin: 0 0 5px;
266 | color: #333;
267 | line-height: 24px;
268 | background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #ffffff), color-stop(100%, #dfdfdf));
269 | background-image: -webkit-linear-gradient(#ffffff, #dfdfdf);
270 | background-image: -moz-linear-gradient(#ffffff, #dfdfdf);
271 | background-image: -o-linear-gradient(#ffffff, #dfdfdf);
272 | background-image: linear-gradient(#ffffff, #dfdfdf);
273 | -webkit-box-shadow: 0 5px 5px 3px rgba(0, 0, 0, 0.2);
274 | -moz-box-shadow: 0 5px 5px 3px rgba(0, 0, 0, 0.2);
275 | box-shadow: 0 5px 5px 3px rgba(0, 0, 0, 0.2)
276 | }
277 | .sidr p {
278 | font-size: 13px;
279 | margin: 0 0 12px
280 | }
281 | .sidr p a {
282 | color: rgba(51, 51, 51, 0.9)
283 | }
284 | .sidr>p {
285 | margin-left: 15px;
286 | margin-right: 15px
287 | }
288 | .sidr ul {
289 | display: block;
290 | margin: 0 0 15px;
291 | padding: 0;
292 | border-top: 1px solid #dfdfdf;
293 | border-bottom: 1px solid #fff
294 | }
295 | .sidr ul li {
296 | display: block;
297 | margin: 0;
298 | line-height: 48px;
299 | border-top: 1px solid #fff;
300 | border-bottom: 1px solid #dfdfdf
301 | }
302 | .sidr ul li:hover, .sidr ul li.active, .sidr ul li.sidr-class-active {
303 | border-top: none;
304 | line-height: 49px
305 | }
306 | .sidr ul li:hover>a, .sidr ul li:hover>span, .sidr ul li.active>a, .sidr ul li.active>span, .sidr ul li.sidr-class-active>a, .sidr ul li.sidr-class-active>span {
307 | -webkit-box-shadow: inset 0 0 15px 3px #ebebeb;
308 | -moz-box-shadow: inset 0 0 15px 3px #ebebeb;
309 | box-shadow: inset 0 0 15px 3px #ebebeb
310 | }
311 | .sidr ul li a, .sidr ul li span {
312 | padding: 0 15px;
313 | display: block;
314 | text-decoration: none;
315 | color: #333
316 | }
317 | .sidr ul li ul {
318 | border-bottom: none;
319 | margin: 0
320 | }
321 | .sidr ul li ul li {
322 | line-height: 40px;
323 | font-size: 13px
324 | }
325 | .sidr ul li ul li:last-child {
326 | border-bottom: none
327 | }
328 | .sidr ul li ul li:hover, .sidr ul li ul li.active, .sidr ul li ul li.sidr-class-active {
329 | border-top: none;
330 | line-height: 41px
331 | }
332 | .sidr ul li ul li:hover>a, .sidr ul li ul li:hover>span, .sidr ul li ul li.active>a, .sidr ul li ul li.active>span, .sidr ul li ul li.sidr-class-active>a, .sidr ul li ul li.sidr-class-active>span {
333 | -webkit-box-shadow: inset 0 0 15px 3px #ebebeb;
334 | -moz-box-shadow: inset 0 0 15px 3px #ebebeb;
335 | box-shadow: inset 0 0 15px 3px #ebebeb
336 | }
337 | .sidr ul li ul li a, .sidr ul li ul li span {
338 | color: rgba(51, 51, 51, 0.8);
339 | padding-left: 30px
340 | }
341 | .sidr form {
342 | margin: 0 15px
343 | }
344 | .sidr label {
345 | font-size: 13px
346 | }
347 | .sidr input[type="text"], .sidr input[type="password"], .sidr input[type="date"], .sidr input[type="datetime"], .sidr input[type="email"], .sidr input[type="number"], .sidr input[type="search"], .sidr input[type="tel"], .sidr input[type="time"], .sidr input[type="url"], .sidr textarea, .sidr select {
348 | width: 100%;
349 | font-size: 13px;
350 | padding: 5px;
351 | -webkit-box-sizing: border-box;
352 | -moz-box-sizing: border-box;
353 | box-sizing: border-box;
354 | margin: 0 0 10px;
355 | -webkit-border-radius: 2px;
356 | -moz-border-radius: 2px;
357 | -ms-border-radius: 2px;
358 | -o-border-radius: 2px;
359 | border-radius: 2px;
360 | border: none;
361 | background: rgba(0, 0, 0, 0.1);
362 | color: rgba(51, 51, 51, 0.6);
363 | display: block;
364 | clear: both
365 | }
366 | .sidr input[type=checkbox] {
367 | width: auto;
368 | display: inline;
369 | clear: none
370 | }
371 | .sidr input[type=button], .sidr input[type=submit] {
372 | color: #f8f8f8;
373 | background: #333
374 | }
375 | .sidr input[type=button]:hover, .sidr input[type=submit]:hover {
376 | background: rgba(51, 51, 51, 0.9)
377 | }
378 |
379 | .blogPosts a {
380 | text-decoration: none;
381 | color:#195A94;
382 | }
383 | .blogPosts .date{
384 | color: #48A682;
385 | font-size: 11px;
386 | line-height: 1.5em;
387 | margin-bottom: 0px;
388 | }
389 | .navbar-default .navbar-toggle .icon-bar {
390 | background-color: #ccc;
391 | }
392 |
393 | .navbar-toggle .icon-bar {
394 | display: block;
395 | width: 22px;
396 | height: 2px;
397 | border-radius: 1px;
398 | }
399 | .btn{
400 | color: #333;
401 | background-color: #fff;
402 | border-color: #ccc;
403 | }
404 | .navbar-default .navbar-toggle {
405 | border-color: #ddd;
406 | }
407 | .navbar-toggle {
408 | position: relative;
409 | float: left;
410 | padding: 9px 10px;
411 | margin-top: 8px;
412 | margin-right: 15px;
413 | margin-bottom: 8px;
414 | background-color: transparent;
415 | background-image: none;
416 | border: 1px solid transparent;
417 | border-radius: 4px;
418 | }
419 | .btn {
420 | display: inline-block;
421 | font-size: 16px;
422 | font-weight: 400;
423 | line-height: 1.42857143;
424 | text-align: center;
425 | white-space: nowrap;
426 | vertical-align: middle;
427 | cursor: pointer;
428 | -webkit-user-select: none;
429 | -moz-user-select: none;
430 | -ms-user-select: none;
431 | user-select: none;
432 | background-image: none;
433 | border: 1px solid transparent;
434 | border-radius: 4px;
435 | width:100%;
436 | }
437 | a#menu {
438 | text-decoration: none;
439 | color: #333;
440 | float:left;
441 | }
442 | .header{
443 | color: #888;
444 | text-align: center;
445 | position: relative;
446 | margin-left: auto;
447 | margin-right: auto;
448 | }
449 | .sr-only{
450 | position: absolute;
451 | width: 1px;
452 | height: 1px;
453 | padding: 0;
454 | margin: -1px;
455 | overflow: hidden;
456 | clip: rect(0,0,0,0);
457 | border: 0;
458 | }
459 | .monav{
460 | position: fixed;
461 | background-color: #fff;
462 | width:100%;
463 | box-shadow: 0 2px 1px rgba(0,0,0, 0.10);
464 | }
--------------------------------------------------------------------------------