Merge branch 'use_javascript_standard_style'
Introduce JavaScript Standard Style Signed-off-by: BoHong Li <a60814billy@gmail.com>
This commit is contained in:
		
						commit
						befa89be8b
					
				
							
								
								
									
										194
									
								
								lib/auth.js
									
									
									
									
									
								
							
							
						
						
									
										194
									
								
								lib/auth.js
									
									
									
									
									
								
							@ -1,24 +1,24 @@
 | 
				
			|||||||
//auth
 | 
					// auth
 | 
				
			||||||
//external modules
 | 
					// external modules
 | 
				
			||||||
var passport = require('passport');
 | 
					var passport = require('passport')
 | 
				
			||||||
var FacebookStrategy = require('passport-facebook').Strategy;
 | 
					var FacebookStrategy = require('passport-facebook').Strategy
 | 
				
			||||||
var TwitterStrategy = require('passport-twitter').Strategy;
 | 
					var TwitterStrategy = require('passport-twitter').Strategy
 | 
				
			||||||
var GithubStrategy = require('passport-github').Strategy;
 | 
					var GithubStrategy = require('passport-github').Strategy
 | 
				
			||||||
var GitlabStrategy = require('passport-gitlab2').Strategy;
 | 
					var GitlabStrategy = require('passport-gitlab2').Strategy
 | 
				
			||||||
var DropboxStrategy = require('passport-dropbox-oauth2').Strategy;
 | 
					var DropboxStrategy = require('passport-dropbox-oauth2').Strategy
 | 
				
			||||||
var GoogleStrategy = require('passport-google-oauth20').Strategy;
 | 
					var GoogleStrategy = require('passport-google-oauth20').Strategy
 | 
				
			||||||
var LdapStrategy = require('passport-ldapauth');
 | 
					var LdapStrategy = require('passport-ldapauth')
 | 
				
			||||||
var LocalStrategy = require('passport-local').Strategy;
 | 
					var LocalStrategy = require('passport-local').Strategy
 | 
				
			||||||
var validator = require('validator');
 | 
					var validator = require('validator')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//core
 | 
					// core
 | 
				
			||||||
var config = require('./config.js');
 | 
					var config = require('./config.js')
 | 
				
			||||||
var logger = require("./logger.js");
 | 
					var logger = require('./logger.js')
 | 
				
			||||||
var models = require("./models");
 | 
					var models = require('./models')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function callback(accessToken, refreshToken, profile, done) {
 | 
					function callback (accessToken, refreshToken, profile, done) {
 | 
				
			||||||
    //logger.info(profile.displayName || profile.username);
 | 
					  // logger.info(profile.displayName || profile.username);
 | 
				
			||||||
    var stringifiedProfile = JSON.stringify(profile);
 | 
					  var stringifiedProfile = JSON.stringify(profile)
 | 
				
			||||||
  models.User.findOrCreate({
 | 
					  models.User.findOrCreate({
 | 
				
			||||||
    where: {
 | 
					    where: {
 | 
				
			||||||
      profileid: profile.id.toString()
 | 
					      profileid: profile.id.toString()
 | 
				
			||||||
@ -30,89 +30,88 @@ function callback(accessToken, refreshToken, profile, done) {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  }).spread(function (user, created) {
 | 
					  }).spread(function (user, created) {
 | 
				
			||||||
    if (user) {
 | 
					    if (user) {
 | 
				
			||||||
            var needSave = false;
 | 
					      var needSave = false
 | 
				
			||||||
            if (user.profile != stringifiedProfile) {
 | 
					      if (user.profile !== stringifiedProfile) {
 | 
				
			||||||
                user.profile = stringifiedProfile;
 | 
					        user.profile = stringifiedProfile
 | 
				
			||||||
                needSave = true;
 | 
					        needSave = true
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
            if (user.accessToken != accessToken) {
 | 
					      if (user.accessToken !== accessToken) {
 | 
				
			||||||
                user.accessToken = accessToken;
 | 
					        user.accessToken = accessToken
 | 
				
			||||||
                needSave = true;
 | 
					        needSave = true
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
            if (user.refreshToken != refreshToken) {
 | 
					      if (user.refreshToken !== refreshToken) {
 | 
				
			||||||
                user.refreshToken = refreshToken;
 | 
					        user.refreshToken = refreshToken
 | 
				
			||||||
                needSave = true;
 | 
					        needSave = true
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      if (needSave) {
 | 
					      if (needSave) {
 | 
				
			||||||
        user.save().then(function () {
 | 
					        user.save().then(function () {
 | 
				
			||||||
                    if (config.debug)
 | 
					          if (config.debug) { logger.info('user login: ' + user.id) }
 | 
				
			||||||
                        logger.info('user login: ' + user.id);
 | 
					          return done(null, user)
 | 
				
			||||||
                    return done(null, user);
 | 
					        })
 | 
				
			||||||
                });
 | 
					 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
                if (config.debug)
 | 
					        if (config.debug) { logger.info('user login: ' + user.id) }
 | 
				
			||||||
                    logger.info('user login: ' + user.id);
 | 
					        return done(null, user)
 | 
				
			||||||
                return done(null, user);
 | 
					 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }).catch(function (err) {
 | 
					  }).catch(function (err) {
 | 
				
			||||||
        logger.error('auth callback failed: ' + err);
 | 
					    logger.error('auth callback failed: ' + err)
 | 
				
			||||||
        return done(err, null);
 | 
					    return done(err, null)
 | 
				
			||||||
    });
 | 
					  })
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//facebook
 | 
					function registerAuthMethod () {
 | 
				
			||||||
if (config.facebook) {
 | 
					// facebook
 | 
				
			||||||
    module.exports = passport.use(new FacebookStrategy({
 | 
					  if (config.facebook) {
 | 
				
			||||||
 | 
					    passport.use(new FacebookStrategy({
 | 
				
			||||||
      clientID: config.facebook.clientID,
 | 
					      clientID: config.facebook.clientID,
 | 
				
			||||||
      clientSecret: config.facebook.clientSecret,
 | 
					      clientSecret: config.facebook.clientSecret,
 | 
				
			||||||
      callbackURL: config.serverurl + '/auth/facebook/callback'
 | 
					      callbackURL: config.serverurl + '/auth/facebook/callback'
 | 
				
			||||||
    }, callback));
 | 
					    }, callback))
 | 
				
			||||||
}
 | 
					  }
 | 
				
			||||||
//twitter
 | 
					// twitter
 | 
				
			||||||
if (config.twitter) {
 | 
					  if (config.twitter) {
 | 
				
			||||||
    passport.use(new TwitterStrategy({
 | 
					    passport.use(new TwitterStrategy({
 | 
				
			||||||
      consumerKey: config.twitter.consumerKey,
 | 
					      consumerKey: config.twitter.consumerKey,
 | 
				
			||||||
      consumerSecret: config.twitter.consumerSecret,
 | 
					      consumerSecret: config.twitter.consumerSecret,
 | 
				
			||||||
      callbackURL: config.serverurl + '/auth/twitter/callback'
 | 
					      callbackURL: config.serverurl + '/auth/twitter/callback'
 | 
				
			||||||
    }, callback));
 | 
					    }, callback))
 | 
				
			||||||
}
 | 
					  }
 | 
				
			||||||
//github
 | 
					// github
 | 
				
			||||||
if (config.github) {
 | 
					  if (config.github) {
 | 
				
			||||||
    passport.use(new GithubStrategy({
 | 
					    passport.use(new GithubStrategy({
 | 
				
			||||||
      clientID: config.github.clientID,
 | 
					      clientID: config.github.clientID,
 | 
				
			||||||
      clientSecret: config.github.clientSecret,
 | 
					      clientSecret: config.github.clientSecret,
 | 
				
			||||||
      callbackURL: config.serverurl + '/auth/github/callback'
 | 
					      callbackURL: config.serverurl + '/auth/github/callback'
 | 
				
			||||||
    }, callback));
 | 
					    }, callback))
 | 
				
			||||||
}
 | 
					  }
 | 
				
			||||||
//gitlab
 | 
					// gitlab
 | 
				
			||||||
if (config.gitlab) {
 | 
					  if (config.gitlab) {
 | 
				
			||||||
    passport.use(new GitlabStrategy({
 | 
					    passport.use(new GitlabStrategy({
 | 
				
			||||||
      baseURL: config.gitlab.baseURL,
 | 
					      baseURL: config.gitlab.baseURL,
 | 
				
			||||||
      clientID: config.gitlab.clientID,
 | 
					      clientID: config.gitlab.clientID,
 | 
				
			||||||
      clientSecret: config.gitlab.clientSecret,
 | 
					      clientSecret: config.gitlab.clientSecret,
 | 
				
			||||||
      callbackURL: config.serverurl + '/auth/gitlab/callback'
 | 
					      callbackURL: config.serverurl + '/auth/gitlab/callback'
 | 
				
			||||||
    }, callback));
 | 
					    }, callback))
 | 
				
			||||||
}
 | 
					  }
 | 
				
			||||||
//dropbox
 | 
					// dropbox
 | 
				
			||||||
if (config.dropbox) {
 | 
					  if (config.dropbox) {
 | 
				
			||||||
    passport.use(new DropboxStrategy({
 | 
					    passport.use(new DropboxStrategy({
 | 
				
			||||||
      apiVersion: '2',
 | 
					      apiVersion: '2',
 | 
				
			||||||
      clientID: config.dropbox.clientID,
 | 
					      clientID: config.dropbox.clientID,
 | 
				
			||||||
      clientSecret: config.dropbox.clientSecret,
 | 
					      clientSecret: config.dropbox.clientSecret,
 | 
				
			||||||
      callbackURL: config.serverurl + '/auth/dropbox/callback'
 | 
					      callbackURL: config.serverurl + '/auth/dropbox/callback'
 | 
				
			||||||
    }, callback));
 | 
					    }, callback))
 | 
				
			||||||
}
 | 
					  }
 | 
				
			||||||
//google
 | 
					// google
 | 
				
			||||||
if (config.google) {
 | 
					  if (config.google) {
 | 
				
			||||||
    passport.use(new GoogleStrategy({
 | 
					    passport.use(new GoogleStrategy({
 | 
				
			||||||
      clientID: config.google.clientID,
 | 
					      clientID: config.google.clientID,
 | 
				
			||||||
      clientSecret: config.google.clientSecret,
 | 
					      clientSecret: config.google.clientSecret,
 | 
				
			||||||
      callbackURL: config.serverurl + '/auth/google/callback'
 | 
					      callbackURL: config.serverurl + '/auth/google/callback'
 | 
				
			||||||
    }, callback));
 | 
					    }, callback))
 | 
				
			||||||
}
 | 
					  }
 | 
				
			||||||
// ldap
 | 
					// ldap
 | 
				
			||||||
if (config.ldap) {
 | 
					  if (config.ldap) {
 | 
				
			||||||
    passport.use(new LdapStrategy({
 | 
					    passport.use(new LdapStrategy({
 | 
				
			||||||
      server: {
 | 
					      server: {
 | 
				
			||||||
        url: config.ldap.url || null,
 | 
					        url: config.ldap.url || null,
 | 
				
			||||||
@ -122,9 +121,9 @@ if (config.ldap) {
 | 
				
			|||||||
        searchFilter: config.ldap.searchFilter || null,
 | 
					        searchFilter: config.ldap.searchFilter || null,
 | 
				
			||||||
        searchAttributes: config.ldap.searchAttributes || null,
 | 
					        searchAttributes: config.ldap.searchAttributes || null,
 | 
				
			||||||
        tlsOptions: config.ldap.tlsOptions || null
 | 
					        tlsOptions: config.ldap.tlsOptions || null
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    },
 | 
					    function (user, done) {
 | 
				
			||||||
    function(user, done) {
 | 
					 | 
				
			||||||
      var profile = {
 | 
					      var profile = {
 | 
				
			||||||
        id: 'LDAP-' + user.uidNumber,
 | 
					        id: 'LDAP-' + user.uidNumber,
 | 
				
			||||||
        username: user.uid,
 | 
					        username: user.uid,
 | 
				
			||||||
@ -132,59 +131,62 @@ if (config.ldap) {
 | 
				
			|||||||
        emails: user.mail ? [user.mail] : [],
 | 
					        emails: user.mail ? [user.mail] : [],
 | 
				
			||||||
        avatarUrl: null,
 | 
					        avatarUrl: null,
 | 
				
			||||||
        profileUrl: null,
 | 
					        profileUrl: null,
 | 
				
			||||||
            provider: 'ldap',
 | 
					        provider: 'ldap'
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
        var stringifiedProfile = JSON.stringify(profile);
 | 
					      var stringifiedProfile = JSON.stringify(profile)
 | 
				
			||||||
      models.User.findOrCreate({
 | 
					      models.User.findOrCreate({
 | 
				
			||||||
        where: {
 | 
					        where: {
 | 
				
			||||||
          profileid: profile.id.toString()
 | 
					          profileid: profile.id.toString()
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        defaults: {
 | 
					        defaults: {
 | 
				
			||||||
                profile: stringifiedProfile,
 | 
					          profile: stringifiedProfile
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }).spread(function (user, created) {
 | 
					      }).spread(function (user, created) {
 | 
				
			||||||
        if (user) {
 | 
					        if (user) {
 | 
				
			||||||
                var needSave = false;
 | 
					          var needSave = false
 | 
				
			||||||
                if (user.profile != stringifiedProfile) {
 | 
					          if (user.profile !== stringifiedProfile) {
 | 
				
			||||||
                    user.profile = stringifiedProfile;
 | 
					            user.profile = stringifiedProfile
 | 
				
			||||||
                    needSave = true;
 | 
					            needSave = true
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
          if (needSave) {
 | 
					          if (needSave) {
 | 
				
			||||||
            user.save().then(function () {
 | 
					            user.save().then(function () {
 | 
				
			||||||
                        if (config.debug)
 | 
					              if (config.debug) { logger.info('user login: ' + user.id) }
 | 
				
			||||||
                            logger.info('user login: ' + user.id);
 | 
					              return done(null, user)
 | 
				
			||||||
                        return done(null, user);
 | 
					            })
 | 
				
			||||||
                    });
 | 
					 | 
				
			||||||
          } else {
 | 
					          } else {
 | 
				
			||||||
                    if (config.debug)
 | 
					            if (config.debug) { logger.info('user login: ' + user.id) }
 | 
				
			||||||
                        logger.info('user login: ' + user.id);
 | 
					            return done(null, user)
 | 
				
			||||||
                    return done(null, user);
 | 
					 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }).catch(function (err) {
 | 
					      }).catch(function (err) {
 | 
				
			||||||
            logger.error('ldap auth failed: ' + err);
 | 
					        logger.error('ldap auth failed: ' + err)
 | 
				
			||||||
            return done(err, null);
 | 
					        return done(err, null)
 | 
				
			||||||
        });
 | 
					      })
 | 
				
			||||||
    }));
 | 
					    }))
 | 
				
			||||||
}
 | 
					  }
 | 
				
			||||||
// email
 | 
					// email
 | 
				
			||||||
if (config.email) {
 | 
					  if (config.email) {
 | 
				
			||||||
    passport.use(new LocalStrategy({
 | 
					    passport.use(new LocalStrategy({
 | 
				
			||||||
      usernameField: 'email'
 | 
					      usernameField: 'email'
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    function(email, password, done) {
 | 
					    function (email, password, done) {
 | 
				
			||||||
        if (!validator.isEmail(email)) return done(null, false);
 | 
					      if (!validator.isEmail(email)) return done(null, false)
 | 
				
			||||||
      models.User.findOne({
 | 
					      models.User.findOne({
 | 
				
			||||||
        where: {
 | 
					        where: {
 | 
				
			||||||
          email: email
 | 
					          email: email
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }).then(function (user) {
 | 
					      }).then(function (user) {
 | 
				
			||||||
            if (!user) return done(null, false);
 | 
					        if (!user) return done(null, false)
 | 
				
			||||||
            if (!user.verifyPassword(password)) return done(null, false);
 | 
					        if (!user.verifyPassword(password)) return done(null, false)
 | 
				
			||||||
            return done(null, user);
 | 
					        return done(null, user)
 | 
				
			||||||
      }).catch(function (err) {
 | 
					      }).catch(function (err) {
 | 
				
			||||||
            logger.error(err);
 | 
					        logger.error(err)
 | 
				
			||||||
            return done(err);
 | 
					        return done(err)
 | 
				
			||||||
        });
 | 
					      })
 | 
				
			||||||
    }));
 | 
					    }))
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module.exports = {
 | 
				
			||||||
 | 
					  registerAuthMethod: registerAuthMethod
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										204
									
								
								lib/config.js
									
									
									
									
									
								
							
							
						
						
									
										204
									
								
								lib/config.js
									
									
									
									
									
								
							@ -1,118 +1,117 @@
 | 
				
			|||||||
// external modules
 | 
					// external modules
 | 
				
			||||||
var fs = require('fs');
 | 
					var fs = require('fs')
 | 
				
			||||||
var path = require('path');
 | 
					var path = require('path')
 | 
				
			||||||
var fs = require('fs');
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
// configs
 | 
					// configs
 | 
				
			||||||
var env = process.env.NODE_ENV || 'development';
 | 
					var env = process.env.NODE_ENV || 'development'
 | 
				
			||||||
var config = require(path.join(__dirname, '..', 'config.json'))[env];
 | 
					var config = require(path.join(__dirname, '..', 'config.json'))[env]
 | 
				
			||||||
var debug = process.env.DEBUG ? (process.env.DEBUG === 'true') : ((typeof config.debug === 'boolean') ? config.debug : (env === 'development'));
 | 
					var debug = process.env.DEBUG ? (process.env.DEBUG === 'true') : ((typeof config.debug === 'boolean') ? config.debug : (env === 'development'))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Create function that reads docker secrets but fails fast in case of a non docker environment
 | 
					// Create function that reads docker secrets but fails fast in case of a non docker environment
 | 
				
			||||||
var handleDockerSecret = fs.existsSync('/run/secrets/') ? function(secret) {
 | 
					var handleDockerSecret = fs.existsSync('/run/secrets/') ? function (secret) {
 | 
				
			||||||
    return fs.existsSync('/run/secrets/' + secret) ? fs.readFileSync('/run/secrets/' + secret) : null;
 | 
					  return fs.existsSync('/run/secrets/' + secret) ? fs.readFileSync('/run/secrets/' + secret) : null
 | 
				
			||||||
} : function() {
 | 
					} : function () {
 | 
				
			||||||
  return null
 | 
					  return null
 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// url
 | 
					 | 
				
			||||||
var domain = process.env.DOMAIN || process.env.HMD_DOMAIN || config.domain || '';
 | 
					 | 
				
			||||||
var urlpath = process.env.URL_PATH || process.env.HMD_URL_PATH || config.urlpath || '';
 | 
					 | 
				
			||||||
var port = process.env.PORT || process.env.HMD_PORT || config.port || 3000;
 | 
					 | 
				
			||||||
var alloworigin = process.env.HMD_ALLOW_ORIGIN ? process.env.HMD_ALLOW_ORIGIN.split(',') : (config.alloworigin || ['localhost']);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var usessl = !!config.usessl;
 | 
					 | 
				
			||||||
var protocolusessl = (usessl === true && typeof process.env.HMD_PROTOCOL_USESSL === 'undefined' && typeof config.protocolusessl === 'undefined')
 | 
					 | 
				
			||||||
     ? true : (process.env.HMD_PROTOCOL_USESSL ? (process.env.HMD_PROTOCOL_USESSL === 'true') : !!config.protocolusessl);
 | 
					 | 
				
			||||||
var urladdport = process.env.HMD_URL_ADDPORT ? (process.env.HMD_URL_ADDPORT === 'true') : !!config.urladdport;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var usecdn = process.env.HMD_USECDN ? (process.env.HMD_USECDN === 'true') : ((typeof config.usecdn === 'boolean') ? config.usecdn : true);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var allowanonymous = process.env.HMD_ALLOW_ANONYMOUS ? (process.env.HMD_ALLOW_ANONYMOUS === 'true') : ((typeof config.allowanonymous === 'boolean') ? config.allowanonymous : true);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var allowfreeurl = process.env.HMD_ALLOW_FREEURL ? (process.env.HMD_ALLOW_FREEURL === 'true') : !!config.allowfreeurl;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var permissions = ['editable', 'limited', 'locked', 'protected', 'private'];
 | 
					 | 
				
			||||||
if (allowanonymous) {
 | 
					 | 
				
			||||||
    permissions.unshift('freely');
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var defaultpermission = process.env.HMD_DEFAULT_PERMISSION || config.defaultpermission;
 | 
					// url
 | 
				
			||||||
defaultpermission = permissions.indexOf(defaultpermission) != -1 ? defaultpermission : 'editable';
 | 
					var domain = process.env.DOMAIN || process.env.HMD_DOMAIN || config.domain || ''
 | 
				
			||||||
 | 
					var urlpath = process.env.URL_PATH || process.env.HMD_URL_PATH || config.urlpath || ''
 | 
				
			||||||
 | 
					var port = process.env.PORT || process.env.HMD_PORT || config.port || 3000
 | 
				
			||||||
 | 
					var alloworigin = process.env.HMD_ALLOW_ORIGIN ? process.env.HMD_ALLOW_ORIGIN.split(',') : (config.alloworigin || ['localhost'])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var usessl = !!config.usessl
 | 
				
			||||||
 | 
					var protocolusessl = (usessl === true && typeof process.env.HMD_PROTOCOL_USESSL === 'undefined' && typeof config.protocolusessl === 'undefined')
 | 
				
			||||||
 | 
					     ? true : (process.env.HMD_PROTOCOL_USESSL ? (process.env.HMD_PROTOCOL_USESSL === 'true') : !!config.protocolusessl)
 | 
				
			||||||
 | 
					var urladdport = process.env.HMD_URL_ADDPORT ? (process.env.HMD_URL_ADDPORT === 'true') : !!config.urladdport
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var usecdn = process.env.HMD_USECDN ? (process.env.HMD_USECDN === 'true') : ((typeof config.usecdn === 'boolean') ? config.usecdn : true)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var allowanonymous = process.env.HMD_ALLOW_ANONYMOUS ? (process.env.HMD_ALLOW_ANONYMOUS === 'true') : ((typeof config.allowanonymous === 'boolean') ? config.allowanonymous : true)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var allowfreeurl = process.env.HMD_ALLOW_FREEURL ? (process.env.HMD_ALLOW_FREEURL === 'true') : !!config.allowfreeurl
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var permissions = ['editable', 'limited', 'locked', 'protected', 'private']
 | 
				
			||||||
 | 
					if (allowanonymous) {
 | 
				
			||||||
 | 
					  permissions.unshift('freely')
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var defaultpermission = process.env.HMD_DEFAULT_PERMISSION || config.defaultpermission
 | 
				
			||||||
 | 
					defaultpermission = permissions.indexOf(defaultpermission) !== -1 ? defaultpermission : 'editable'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// db
 | 
					// db
 | 
				
			||||||
var dburl = process.env.HMD_DB_URL || process.env.DATABASE_URL || config.dburl;
 | 
					var dburl = process.env.HMD_DB_URL || process.env.DATABASE_URL || config.dburl
 | 
				
			||||||
var db = config.db || {};
 | 
					var db = config.db || {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ssl path
 | 
					// ssl path
 | 
				
			||||||
var sslkeypath = (fs.existsSync('/run/secrets/key.pem') ? '/run/secrets/key.pem' : null) || config.sslkeypath || '';
 | 
					var sslkeypath = (fs.existsSync('/run/secrets/key.pem') ? '/run/secrets/key.pem' : null) || config.sslkeypath || ''
 | 
				
			||||||
var sslcertpath = (fs.existsSync('/run/secrets/cert.pem') ? '/run/secrets/cert.pem' : null) || config.sslcertpath || '';
 | 
					var sslcertpath = (fs.existsSync('/run/secrets/cert.pem') ? '/run/secrets/cert.pem' : null) || config.sslcertpath || ''
 | 
				
			||||||
var sslcapath = (fs.existsSync('/run/secrets/ca.pem') ? '/run/secrets/ca.pem' : null) || config.sslcapath || '';
 | 
					var sslcapath = (fs.existsSync('/run/secrets/ca.pem') ? '/run/secrets/ca.pem' : null) || config.sslcapath || ''
 | 
				
			||||||
var dhparampath = (fs.existsSync('/run/secrets/dhparam.pem') ? '/run/secrets/dhparam.pem' : null) || config.dhparampath || '';
 | 
					var dhparampath = (fs.existsSync('/run/secrets/dhparam.pem') ? '/run/secrets/dhparam.pem' : null) || config.dhparampath || ''
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// other path
 | 
					// other path
 | 
				
			||||||
var tmppath = config.tmppath || './tmp';
 | 
					var tmppath = config.tmppath || './tmp'
 | 
				
			||||||
var defaultnotepath = config.defaultnotepath || './public/default.md';
 | 
					var defaultnotepath = config.defaultnotepath || './public/default.md'
 | 
				
			||||||
var docspath = config.docspath || './public/docs';
 | 
					var docspath = config.docspath || './public/docs'
 | 
				
			||||||
var indexpath = config.indexpath || './public/views/index.ejs';
 | 
					var indexpath = config.indexpath || './public/views/index.ejs'
 | 
				
			||||||
var hackmdpath = config.hackmdpath || './public/views/hackmd.ejs';
 | 
					var hackmdpath = config.hackmdpath || './public/views/hackmd.ejs'
 | 
				
			||||||
var errorpath = config.errorpath || './public/views/error.ejs';
 | 
					var errorpath = config.errorpath || './public/views/error.ejs'
 | 
				
			||||||
var prettypath = config.prettypath || './public/views/pretty.ejs';
 | 
					var prettypath = config.prettypath || './public/views/pretty.ejs'
 | 
				
			||||||
var slidepath = config.slidepath || './public/views/slide.ejs';
 | 
					var slidepath = config.slidepath || './public/views/slide.ejs'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// session
 | 
					// session
 | 
				
			||||||
var sessionname = config.sessionname || 'connect.sid';
 | 
					var sessionname = config.sessionname || 'connect.sid'
 | 
				
			||||||
var sessionsecret = handleDockerSecret('sessionsecret') || config.sessionsecret || 'secret';
 | 
					var sessionsecret = handleDockerSecret('sessionsecret') || config.sessionsecret || 'secret'
 | 
				
			||||||
var sessionlife = config.sessionlife || 14 * 24 * 60 * 60 * 1000; //14 days
 | 
					var sessionlife = config.sessionlife || 14 * 24 * 60 * 60 * 1000 // 14 days
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// static files
 | 
					// static files
 | 
				
			||||||
var staticcachetime = config.staticcachetime || 1 * 24 * 60 * 60 * 1000; // 1 day
 | 
					var staticcachetime = config.staticcachetime || 1 * 24 * 60 * 60 * 1000 // 1 day
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// socket.io
 | 
					// socket.io
 | 
				
			||||||
var heartbeatinterval = config.heartbeatinterval || 5000;
 | 
					var heartbeatinterval = config.heartbeatinterval || 5000
 | 
				
			||||||
var heartbeattimeout = config.heartbeattimeout || 10000;
 | 
					var heartbeattimeout = config.heartbeattimeout || 10000
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// document
 | 
					// document
 | 
				
			||||||
var documentmaxlength = config.documentmaxlength || 100000;
 | 
					var documentmaxlength = config.documentmaxlength || 100000
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// image upload setting, available options are imgur/s3/filesystem
 | 
					// image upload setting, available options are imgur/s3/filesystem
 | 
				
			||||||
var imageUploadType = process.env.HMD_IMAGE_UPLOAD_TYPE || config.imageUploadType || 'imgur';
 | 
					var imageUploadType = process.env.HMD_IMAGE_UPLOAD_TYPE || config.imageUploadType || 'imgur'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
config.s3 = config.s3 || {};
 | 
					config.s3 = config.s3 || {}
 | 
				
			||||||
var s3 = {
 | 
					var s3 = {
 | 
				
			||||||
  accessKeyId: handleDockerSecret('s3_acccessKeyId') || process.env.HMD_S3_ACCESS_KEY_ID || config.s3.accessKeyId,
 | 
					  accessKeyId: handleDockerSecret('s3_acccessKeyId') || process.env.HMD_S3_ACCESS_KEY_ID || config.s3.accessKeyId,
 | 
				
			||||||
  secretAccessKey: handleDockerSecret('s3_secretAccessKey') || process.env.HMD_S3_SECRET_ACCESS_KEY || config.s3.secretAccessKey,
 | 
					  secretAccessKey: handleDockerSecret('s3_secretAccessKey') || process.env.HMD_S3_SECRET_ACCESS_KEY || config.s3.secretAccessKey,
 | 
				
			||||||
  region: process.env.HMD_S3_REGION || config.s3.region
 | 
					  region: process.env.HMD_S3_REGION || config.s3.region
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
var s3bucket = process.env.HMD_S3_BUCKET || config.s3.bucket;
 | 
					var s3bucket = process.env.HMD_S3_BUCKET || config.s3.bucket
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// auth
 | 
					// auth
 | 
				
			||||||
var facebook = (process.env.HMD_FACEBOOK_CLIENTID && process.env.HMD_FACEBOOK_CLIENTSECRET || fs.existsSync('/run/secrets/facebook_clientID') && fs.existsSync('/run/secrets/facebook_clientSecret')) ? {
 | 
					var facebook = ((process.env.HMD_FACEBOOK_CLIENTID && process.env.HMD_FACEBOOK_CLIENTSECRET) || (fs.existsSync('/run/secrets/facebook_clientID') && fs.existsSync('/run/secrets/facebook_clientSecret'))) ? {
 | 
				
			||||||
  clientID: handleDockerSecret('facebook_clientID') || process.env.HMD_FACEBOOK_CLIENTID,
 | 
					  clientID: handleDockerSecret('facebook_clientID') || process.env.HMD_FACEBOOK_CLIENTID,
 | 
				
			||||||
  clientSecret: handleDockerSecret('facebook_clientSecret') || process.env.HMD_FACEBOOK_CLIENTSECRET
 | 
					  clientSecret: handleDockerSecret('facebook_clientSecret') || process.env.HMD_FACEBOOK_CLIENTSECRET
 | 
				
			||||||
} : config.facebook || false;
 | 
					} : config.facebook || false
 | 
				
			||||||
var twitter = (process.env.HMD_TWITTER_CONSUMERKEY && process.env.HMD_TWITTER_CONSUMERSECRET || fs.existsSync('/run/secrets/twitter_consumerKey') && fs.existsSync('/run/secrets/twitter_consumerSecret')) ? {
 | 
					var twitter = ((process.env.HMD_TWITTER_CONSUMERKEY && process.env.HMD_TWITTER_CONSUMERSECRET) || (fs.existsSync('/run/secrets/twitter_consumerKey') && fs.existsSync('/run/secrets/twitter_consumerSecret'))) ? {
 | 
				
			||||||
  consumerKey: handleDockerSecret('twitter_consumerKey') || process.env.HMD_TWITTER_CONSUMERKEY,
 | 
					  consumerKey: handleDockerSecret('twitter_consumerKey') || process.env.HMD_TWITTER_CONSUMERKEY,
 | 
				
			||||||
  consumerSecret: handleDockerSecret('twitter_consumerSecret') || process.env.HMD_TWITTER_CONSUMERSECRET
 | 
					  consumerSecret: handleDockerSecret('twitter_consumerSecret') || process.env.HMD_TWITTER_CONSUMERSECRET
 | 
				
			||||||
} : config.twitter || false;
 | 
					} : config.twitter || false
 | 
				
			||||||
var github = (process.env.HMD_GITHUB_CLIENTID && process.env.HMD_GITHUB_CLIENTSECRET || fs.existsSync('/run/secrets/github_clientID') && fs.existsSync('/run/secrets/github_clientSecret')) ? {
 | 
					var github = ((process.env.HMD_GITHUB_CLIENTID && process.env.HMD_GITHUB_CLIENTSECRET) || (fs.existsSync('/run/secrets/github_clientID') && fs.existsSync('/run/secrets/github_clientSecret'))) ? {
 | 
				
			||||||
  clientID: handleDockerSecret('github_clientID') || process.env.HMD_GITHUB_CLIENTID,
 | 
					  clientID: handleDockerSecret('github_clientID') || process.env.HMD_GITHUB_CLIENTID,
 | 
				
			||||||
  clientSecret: handleDockerSecret('github_clientSecret') || process.env.HMD_GITHUB_CLIENTSECRET
 | 
					  clientSecret: handleDockerSecret('github_clientSecret') || process.env.HMD_GITHUB_CLIENTSECRET
 | 
				
			||||||
} : config.github || false;
 | 
					} : config.github || false
 | 
				
			||||||
var gitlab = (process.env.HMD_GITLAB_CLIENTID && process.env.HMD_GITLAB_CLIENTSECRET || fs.existsSync('/run/secrets/gitlab_clientID') && fs.existsSync('/run/secrets/gitlab_clientSecret')) ? {
 | 
					var gitlab = ((process.env.HMD_GITLAB_CLIENTID && process.env.HMD_GITLAB_CLIENTSECRET) || (fs.existsSync('/run/secrets/gitlab_clientID') && fs.existsSync('/run/secrets/gitlab_clientSecret'))) ? {
 | 
				
			||||||
  baseURL: process.env.HMD_GITLAB_BASEURL,
 | 
					  baseURL: process.env.HMD_GITLAB_BASEURL,
 | 
				
			||||||
  clientID: handleDockerSecret('gitlab_clientID') || process.env.HMD_GITLAB_CLIENTID,
 | 
					  clientID: handleDockerSecret('gitlab_clientID') || process.env.HMD_GITLAB_CLIENTID,
 | 
				
			||||||
  clientSecret: handleDockerSecret('gitlab_clientSecret') || process.env.HMD_GITLAB_CLIENTSECRET
 | 
					  clientSecret: handleDockerSecret('gitlab_clientSecret') || process.env.HMD_GITLAB_CLIENTSECRET
 | 
				
			||||||
} : config.gitlab || false;
 | 
					} : config.gitlab || false
 | 
				
			||||||
var dropbox = ((process.env.HMD_DROPBOX_CLIENTID && process.env.HMD_DROPBOX_CLIENTSECRET) || (fs.existsSync('/run/secrets/dropbox_clientID') && fs.existsSync('/run/secrets/dropbox_clientSecret'))) ? {
 | 
					var dropbox = ((process.env.HMD_DROPBOX_CLIENTID && process.env.HMD_DROPBOX_CLIENTSECRET) || (fs.existsSync('/run/secrets/dropbox_clientID') && fs.existsSync('/run/secrets/dropbox_clientSecret'))) ? {
 | 
				
			||||||
  clientID: handleDockerSecret('dropbox_clientID') || process.env.HMD_DROPBOX_CLIENTID,
 | 
					  clientID: handleDockerSecret('dropbox_clientID') || process.env.HMD_DROPBOX_CLIENTID,
 | 
				
			||||||
  clientSecret: handleDockerSecret('dropbox_clientSecret') || process.env.HMD_DROPBOX_CLIENTSECRET
 | 
					  clientSecret: handleDockerSecret('dropbox_clientSecret') || process.env.HMD_DROPBOX_CLIENTSECRET
 | 
				
			||||||
} : (config.dropbox && config.dropbox.clientID && config.dropbox.clientSecret && config.dropbox) || false;
 | 
					} : (config.dropbox && config.dropbox.clientID && config.dropbox.clientSecret && config.dropbox) || false
 | 
				
			||||||
var google = ((process.env.HMD_GOOGLE_CLIENTID && process.env.HMD_GOOGLE_CLIENTSECRET)
 | 
					var google = ((process.env.HMD_GOOGLE_CLIENTID && process.env.HMD_GOOGLE_CLIENTSECRET) ||
 | 
				
			||||||
              || (fs.existsSync('/run/secrets/google_clientID') && fs.existsSync('/run/secrets/google_clientSecret'))) ? {
 | 
					              (fs.existsSync('/run/secrets/google_clientID') && fs.existsSync('/run/secrets/google_clientSecret'))) ? {
 | 
				
			||||||
                clientID: handleDockerSecret('google_clientID') || process.env.HMD_GOOGLE_CLIENTID,
 | 
					                clientID: handleDockerSecret('google_clientID') || process.env.HMD_GOOGLE_CLIENTID,
 | 
				
			||||||
                clientSecret: handleDockerSecret('google_clientSecret') || process.env.HMD_GOOGLE_CLIENTSECRET
 | 
					                clientSecret: handleDockerSecret('google_clientSecret') || process.env.HMD_GOOGLE_CLIENTSECRET
 | 
				
			||||||
} : (config.google && config.google.clientID && config.google.clientSecret && config.google) || false;
 | 
					              } : (config.google && config.google.clientID && config.google.clientSecret && config.google) || false
 | 
				
			||||||
var ldap = config.ldap || ((
 | 
					var ldap = config.ldap || ((
 | 
				
			||||||
    process.env.HMD_LDAP_URL ||
 | 
					    process.env.HMD_LDAP_URL ||
 | 
				
			||||||
    process.env.HMD_LDAP_BINDDN ||
 | 
					    process.env.HMD_LDAP_BINDDN ||
 | 
				
			||||||
@ -123,59 +122,50 @@ var ldap = config.ldap || ((
 | 
				
			|||||||
    process.env.HMD_LDAP_SEARCHATTRIBUTES ||
 | 
					    process.env.HMD_LDAP_SEARCHATTRIBUTES ||
 | 
				
			||||||
    process.env.HMD_LDAP_TLS_CA ||
 | 
					    process.env.HMD_LDAP_TLS_CA ||
 | 
				
			||||||
    process.env.HMD_LDAP_PROVIDERNAME
 | 
					    process.env.HMD_LDAP_PROVIDERNAME
 | 
				
			||||||
) ? {} : false);
 | 
					) ? {} : false)
 | 
				
			||||||
if (process.env.HMD_LDAP_URL)
 | 
					if (process.env.HMD_LDAP_URL) { ldap.url = process.env.HMD_LDAP_URL }
 | 
				
			||||||
    ldap.url = process.env.HMD_LDAP_URL;
 | 
					if (process.env.HMD_LDAP_BINDDN) { ldap.bindDn = process.env.HMD_LDAP_BINDDN }
 | 
				
			||||||
if (process.env.HMD_LDAP_BINDDN)
 | 
					if (process.env.HMD_LDAP_BINDCREDENTIALS) { ldap.bindCredentials = process.env.HMD_LDAP_BINDCREDENTIALS }
 | 
				
			||||||
    ldap.bindDn = process.env.HMD_LDAP_BINDDN;
 | 
					if (process.env.HMD_LDAP_TOKENSECRET) { ldap.tokenSecret = process.env.HMD_LDAP_TOKENSECRET }
 | 
				
			||||||
if (process.env.HMD_LDAP_BINDCREDENTIALS)
 | 
					if (process.env.HMD_LDAP_SEARCHBASE) { ldap.searchBase = process.env.HMD_LDAP_SEARCHBASE }
 | 
				
			||||||
    ldap.bindCredentials = process.env.HMD_LDAP_BINDCREDENTIALS;
 | 
					if (process.env.HMD_LDAP_SEARCHFILTER) { ldap.searchFilter = process.env.HMD_LDAP_SEARCHFILTER }
 | 
				
			||||||
if (process.env.HMD_LDAP_TOKENSECRET)
 | 
					if (process.env.HMD_LDAP_SEARCHATTRIBUTES) { ldap.searchAttributes = process.env.HMD_LDAP_SEARCHATTRIBUTES }
 | 
				
			||||||
    ldap.tokenSecret = process.env.HMD_LDAP_TOKENSECRET;
 | 
					 | 
				
			||||||
if (process.env.HMD_LDAP_SEARCHBASE)
 | 
					 | 
				
			||||||
    ldap.searchBase = process.env.HMD_LDAP_SEARCHBASE;
 | 
					 | 
				
			||||||
if (process.env.HMD_LDAP_SEARCHFILTER)
 | 
					 | 
				
			||||||
    ldap.searchFilter = process.env.HMD_LDAP_SEARCHFILTER;
 | 
					 | 
				
			||||||
if (process.env.HMD_LDAP_SEARCHATTRIBUTES)
 | 
					 | 
				
			||||||
    ldap.searchAttributes = process.env.HMD_LDAP_SEARCHATTRIBUTES;
 | 
					 | 
				
			||||||
if (process.env.HMD_LDAP_TLS_CA) {
 | 
					if (process.env.HMD_LDAP_TLS_CA) {
 | 
				
			||||||
  var ca = {
 | 
					  var ca = {
 | 
				
			||||||
    ca: process.env.HMD_LDAP_TLS_CA.split(',')
 | 
					    ca: process.env.HMD_LDAP_TLS_CA.split(',')
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
    ldap.tlsOptions = ldap.tlsOptions ? Object.assign(ldap.tlsOptions, ca) : ca;
 | 
					  ldap.tlsOptions = ldap.tlsOptions ? Object.assign(ldap.tlsOptions, ca) : ca
 | 
				
			||||||
  if (Array.isArray(ldap.tlsOptions.ca) && ldap.tlsOptions.ca.length > 0) {
 | 
					  if (Array.isArray(ldap.tlsOptions.ca) && ldap.tlsOptions.ca.length > 0) {
 | 
				
			||||||
        var i, len, results;
 | 
					    var i, len, results
 | 
				
			||||||
        results = [];
 | 
					    results = []
 | 
				
			||||||
    for (i = 0, len = ldap.tlsOptions.ca.length; i < len; i++) {
 | 
					    for (i = 0, len = ldap.tlsOptions.ca.length; i < len; i++) {
 | 
				
			||||||
            results.push(fs.readFileSync(ldap.tlsOptions.ca[i], 'utf8'));
 | 
					      results.push(fs.readFileSync(ldap.tlsOptions.ca[i], 'utf8'))
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
        ldap.tlsOptions.ca = results;
 | 
					    ldap.tlsOptions.ca = results
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
if (process.env.HMD_LDAP_PROVIDERNAME) {
 | 
					if (process.env.HMD_LDAP_PROVIDERNAME) {
 | 
				
			||||||
    ldap.providerName = process.env.HMD_LDAP_PROVIDERNAME;
 | 
					  ldap.providerName = process.env.HMD_LDAP_PROVIDERNAME
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
var imgur = handleDockerSecret('imgur_clientid') || process.env.HMD_IMGUR_CLIENTID || config.imgur || false;
 | 
					var imgur = handleDockerSecret('imgur_clientid') || process.env.HMD_IMGUR_CLIENTID || config.imgur || false
 | 
				
			||||||
var email = process.env.HMD_EMAIL ? (process.env.HMD_EMAIL === 'true') : !!config.email;
 | 
					var email = process.env.HMD_EMAIL ? (process.env.HMD_EMAIL === 'true') : !!config.email
 | 
				
			||||||
var allowemailregister = process.env.HMD_ALLOW_EMAIL_REGISTER ? (process.env.HMD_ALLOW_EMAIL_REGISTER === 'true') : ((typeof config.allowemailregister === 'boolean') ? config.allowemailregister : true);
 | 
					var allowemailregister = process.env.HMD_ALLOW_EMAIL_REGISTER ? (process.env.HMD_ALLOW_EMAIL_REGISTER === 'true') : ((typeof config.allowemailregister === 'boolean') ? config.allowemailregister : true)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function getserverurl() {
 | 
					function getserverurl () {
 | 
				
			||||||
    var url = '';
 | 
					  var url = ''
 | 
				
			||||||
  if (domain) {
 | 
					  if (domain) {
 | 
				
			||||||
        var protocol = protocolusessl ? 'https://' : 'http://';
 | 
					    var protocol = protocolusessl ? 'https://' : 'http://'
 | 
				
			||||||
        url = protocol + domain;
 | 
					    url = protocol + domain
 | 
				
			||||||
        if (urladdport && ((usessl && port != 443) || (!usessl && port != 80)))
 | 
					    if (urladdport && ((usessl && port !== 443) || (!usessl && port !== 80))) { url += ':' + port }
 | 
				
			||||||
            url += ':' + port;
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
    if (urlpath)
 | 
					  if (urlpath) { url += '/' + urlpath }
 | 
				
			||||||
        url += '/' + urlpath;
 | 
					  return url
 | 
				
			||||||
    return url;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var version = '0.5.0';
 | 
					var version = '0.5.0'
 | 
				
			||||||
var minimumCompatibleVersion = '0.5.0';
 | 
					var minimumCompatibleVersion = '0.5.0'
 | 
				
			||||||
var maintenance = true;
 | 
					var maintenance = true
 | 
				
			||||||
var cwd = path.join(__dirname, '..');
 | 
					var cwd = path.join(__dirname, '..')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
module.exports = {
 | 
					module.exports = {
 | 
				
			||||||
  version: version,
 | 
					  version: version,
 | 
				
			||||||
@ -225,4 +215,4 @@ module.exports = {
 | 
				
			|||||||
  imageUploadType: imageUploadType,
 | 
					  imageUploadType: imageUploadType,
 | 
				
			||||||
  s3: s3,
 | 
					  s3: s3,
 | 
				
			||||||
  s3bucket: s3bucket
 | 
					  s3bucket: s3bucket
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										187
									
								
								lib/history.js
									
									
									
									
									
								
							
							
						
						
									
										187
									
								
								lib/history.js
									
									
									
									
									
								
							@ -1,42 +1,44 @@
 | 
				
			|||||||
//history
 | 
					// history
 | 
				
			||||||
//external modules
 | 
					// external modules
 | 
				
			||||||
var async = require('async');
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
//core
 | 
					// core
 | 
				
			||||||
var config = require("./config.js");
 | 
					var config = require('./config.js')
 | 
				
			||||||
var logger = require("./logger.js");
 | 
					var logger = require('./logger.js')
 | 
				
			||||||
var response = require("./response.js");
 | 
					var response = require('./response.js')
 | 
				
			||||||
var models = require("./models");
 | 
					var models = require('./models')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//public
 | 
					// public
 | 
				
			||||||
var History = {
 | 
					var History = {
 | 
				
			||||||
  historyGet: historyGet,
 | 
					  historyGet: historyGet,
 | 
				
			||||||
  historyPost: historyPost,
 | 
					  historyPost: historyPost,
 | 
				
			||||||
  historyDelete: historyDelete,
 | 
					  historyDelete: historyDelete,
 | 
				
			||||||
  updateHistory: updateHistory
 | 
					  updateHistory: updateHistory
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function getHistory(userid, callback) {
 | 
					function getHistory (userid, callback) {
 | 
				
			||||||
  models.User.findOne({
 | 
					  models.User.findOne({
 | 
				
			||||||
    where: {
 | 
					    where: {
 | 
				
			||||||
      id: userid
 | 
					      id: userid
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }).then(function (user) {
 | 
					  }).then(function (user) {
 | 
				
			||||||
        if (!user)
 | 
					    if (!user) {
 | 
				
			||||||
            return callback(null, null);
 | 
					      return callback(null, null)
 | 
				
			||||||
        var history = {};
 | 
					    }
 | 
				
			||||||
        if (user.history)
 | 
					    var history = {}
 | 
				
			||||||
            history = parseHistoryToObject(JSON.parse(user.history));
 | 
					    if (user.history) {
 | 
				
			||||||
        if (config.debug)
 | 
					      history = parseHistoryToObject(JSON.parse(user.history))
 | 
				
			||||||
            logger.info('read history success: ' + user.id);
 | 
					    }
 | 
				
			||||||
        return callback(null, history);
 | 
					    if (config.debug) {
 | 
				
			||||||
 | 
					      logger.info('read history success: ' + user.id)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return callback(null, history)
 | 
				
			||||||
  }).catch(function (err) {
 | 
					  }).catch(function (err) {
 | 
				
			||||||
        logger.error('read history failed: ' + err);
 | 
					    logger.error('read history failed: ' + err)
 | 
				
			||||||
        return callback(err, null);
 | 
					    return callback(err, null)
 | 
				
			||||||
    });
 | 
					  })
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function setHistory(userid, history, callback) {
 | 
					function setHistory (userid, history, callback) {
 | 
				
			||||||
  models.User.update({
 | 
					  models.User.update({
 | 
				
			||||||
    history: JSON.stringify(parseHistoryToArray(history))
 | 
					    history: JSON.stringify(parseHistoryToArray(history))
 | 
				
			||||||
  }, {
 | 
					  }, {
 | 
				
			||||||
@ -44,129 +46,130 @@ function setHistory(userid, history, callback) {
 | 
				
			|||||||
      id: userid
 | 
					      id: userid
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }).then(function (count) {
 | 
					  }).then(function (count) {
 | 
				
			||||||
        return callback(null, count);
 | 
					    return callback(null, count)
 | 
				
			||||||
  }).catch(function (err) {
 | 
					  }).catch(function (err) {
 | 
				
			||||||
        logger.error('set history failed: ' + err);
 | 
					    logger.error('set history failed: ' + err)
 | 
				
			||||||
        return callback(err, null);
 | 
					    return callback(err, null)
 | 
				
			||||||
    });
 | 
					  })
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function updateHistory(userid, noteId, document, time) {
 | 
					function updateHistory (userid, noteId, document, time) {
 | 
				
			||||||
  if (userid && noteId && typeof document !== 'undefined') {
 | 
					  if (userid && noteId && typeof document !== 'undefined') {
 | 
				
			||||||
    getHistory(userid, function (err, history) {
 | 
					    getHistory(userid, function (err, history) {
 | 
				
			||||||
            if (err || !history) return;
 | 
					      if (err || !history) return
 | 
				
			||||||
      if (!history[noteId]) {
 | 
					      if (!history[noteId]) {
 | 
				
			||||||
                history[noteId] = {};
 | 
					        history[noteId] = {}
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
            var noteHistory = history[noteId];
 | 
					      var noteHistory = history[noteId]
 | 
				
			||||||
            var noteInfo = models.Note.parseNoteInfo(document);
 | 
					      var noteInfo = models.Note.parseNoteInfo(document)
 | 
				
			||||||
            noteHistory.id = noteId;
 | 
					      noteHistory.id = noteId
 | 
				
			||||||
            noteHistory.text = noteInfo.title;
 | 
					      noteHistory.text = noteInfo.title
 | 
				
			||||||
            noteHistory.time = time || Date.now();
 | 
					      noteHistory.time = time || Date.now()
 | 
				
			||||||
            noteHistory.tags = noteInfo.tags;
 | 
					      noteHistory.tags = noteInfo.tags
 | 
				
			||||||
      setHistory(userid, history, function (err, count) {
 | 
					      setHistory(userid, history, function (err, count) {
 | 
				
			||||||
                return;
 | 
					        if (err) {
 | 
				
			||||||
            });
 | 
					          logger.log(err)
 | 
				
			||||||
        });
 | 
					        }
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function parseHistoryToArray(history) {
 | 
					function parseHistoryToArray (history) {
 | 
				
			||||||
    var _history = [];
 | 
					  var _history = []
 | 
				
			||||||
  Object.keys(history).forEach(function (key) {
 | 
					  Object.keys(history).forEach(function (key) {
 | 
				
			||||||
        var item = history[key];
 | 
					    var item = history[key]
 | 
				
			||||||
        _history.push(item);
 | 
					    _history.push(item)
 | 
				
			||||||
    });
 | 
					  })
 | 
				
			||||||
    return _history;
 | 
					  return _history
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function parseHistoryToObject(history) {
 | 
					function parseHistoryToObject (history) {
 | 
				
			||||||
    var _history = {};
 | 
					  var _history = {}
 | 
				
			||||||
  for (var i = 0, l = history.length; i < l; i++) {
 | 
					  for (var i = 0, l = history.length; i < l; i++) {
 | 
				
			||||||
        var item = history[i];
 | 
					    var item = history[i]
 | 
				
			||||||
        _history[item.id] = item;
 | 
					    _history[item.id] = item
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
    return _history;
 | 
					  return _history
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function historyGet(req, res) {
 | 
					function historyGet (req, res) {
 | 
				
			||||||
  if (req.isAuthenticated()) {
 | 
					  if (req.isAuthenticated()) {
 | 
				
			||||||
    getHistory(req.user.id, function (err, history) {
 | 
					    getHistory(req.user.id, function (err, history) {
 | 
				
			||||||
            if (err) return response.errorInternalError(res);
 | 
					      if (err) return response.errorInternalError(res)
 | 
				
			||||||
            if (!history) return response.errorNotFound(res);
 | 
					      if (!history) return response.errorNotFound(res)
 | 
				
			||||||
      res.send({
 | 
					      res.send({
 | 
				
			||||||
        history: parseHistoryToArray(history)
 | 
					        history: parseHistoryToArray(history)
 | 
				
			||||||
            });
 | 
					      })
 | 
				
			||||||
        });
 | 
					    })
 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
        return response.errorForbidden(res);
 | 
					    return response.errorForbidden(res)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function historyPost(req, res) {
 | 
					function historyPost (req, res) {
 | 
				
			||||||
  if (req.isAuthenticated()) {
 | 
					  if (req.isAuthenticated()) {
 | 
				
			||||||
        var noteId = req.params.noteId;
 | 
					    var noteId = req.params.noteId
 | 
				
			||||||
    if (!noteId) {
 | 
					    if (!noteId) {
 | 
				
			||||||
            if (typeof req.body['history'] === 'undefined') return response.errorBadRequest(res);
 | 
					      if (typeof req.body['history'] === 'undefined') return response.errorBadRequest(res)
 | 
				
			||||||
            if (config.debug)
 | 
					      if (config.debug) { logger.info('SERVER received history from [' + req.user.id + ']: ' + req.body.history) }
 | 
				
			||||||
                logger.info('SERVER received history from [' + req.user.id + ']: ' + req.body.history);
 | 
					 | 
				
			||||||
      try {
 | 
					      try {
 | 
				
			||||||
                var history = JSON.parse(req.body.history);
 | 
					        var history = JSON.parse(req.body.history)
 | 
				
			||||||
      } catch (err) {
 | 
					      } catch (err) {
 | 
				
			||||||
                return response.errorBadRequest(res);
 | 
					        return response.errorBadRequest(res)
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      if (Array.isArray(history)) {
 | 
					      if (Array.isArray(history)) {
 | 
				
			||||||
        setHistory(req.user.id, history, function (err, count) {
 | 
					        setHistory(req.user.id, history, function (err, count) {
 | 
				
			||||||
                    if (err) return response.errorInternalError(res);
 | 
					          if (err) return response.errorInternalError(res)
 | 
				
			||||||
                    res.end();
 | 
					          res.end()
 | 
				
			||||||
                });
 | 
					        })
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
                return response.errorBadRequest(res);
 | 
					        return response.errorBadRequest(res)
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
            if (typeof req.body['pinned'] === 'undefined') return response.errorBadRequest(res);
 | 
					      if (typeof req.body['pinned'] === 'undefined') return response.errorBadRequest(res)
 | 
				
			||||||
      getHistory(req.user.id, function (err, history) {
 | 
					      getHistory(req.user.id, function (err, history) {
 | 
				
			||||||
                if (err) return response.errorInternalError(res);
 | 
					        if (err) return response.errorInternalError(res)
 | 
				
			||||||
                if (!history) return response.errorNotFound(res);
 | 
					        if (!history) return response.errorNotFound(res)
 | 
				
			||||||
                if (!history[noteId]) return response.errorNotFound(res);
 | 
					        if (!history[noteId]) return response.errorNotFound(res)
 | 
				
			||||||
        if (req.body.pinned === 'true' || req.body.pinned === 'false') {
 | 
					        if (req.body.pinned === 'true' || req.body.pinned === 'false') {
 | 
				
			||||||
                    history[noteId].pinned = (req.body.pinned === 'true');
 | 
					          history[noteId].pinned = (req.body.pinned === 'true')
 | 
				
			||||||
          setHistory(req.user.id, history, function (err, count) {
 | 
					          setHistory(req.user.id, history, function (err, count) {
 | 
				
			||||||
                        if (err) return response.errorInternalError(res);
 | 
					            if (err) return response.errorInternalError(res)
 | 
				
			||||||
                        res.end();
 | 
					            res.end()
 | 
				
			||||||
                    });
 | 
					          })
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
                    return response.errorBadRequest(res);
 | 
					          return response.errorBadRequest(res)
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
            });
 | 
					      })
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
        return response.errorForbidden(res);
 | 
					    return response.errorForbidden(res)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function historyDelete(req, res) {
 | 
					function historyDelete (req, res) {
 | 
				
			||||||
  if (req.isAuthenticated()) {
 | 
					  if (req.isAuthenticated()) {
 | 
				
			||||||
        var noteId = req.params.noteId;
 | 
					    var noteId = req.params.noteId
 | 
				
			||||||
    if (!noteId) {
 | 
					    if (!noteId) {
 | 
				
			||||||
      setHistory(req.user.id, [], function (err, count) {
 | 
					      setHistory(req.user.id, [], function (err, count) {
 | 
				
			||||||
                if (err) return response.errorInternalError(res);
 | 
					        if (err) return response.errorInternalError(res)
 | 
				
			||||||
                res.end();
 | 
					        res.end()
 | 
				
			||||||
            });
 | 
					      })
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      getHistory(req.user.id, function (err, history) {
 | 
					      getHistory(req.user.id, function (err, history) {
 | 
				
			||||||
                if (err) return response.errorInternalError(res);
 | 
					        if (err) return response.errorInternalError(res)
 | 
				
			||||||
                if (!history) return response.errorNotFound(res);
 | 
					        if (!history) return response.errorNotFound(res)
 | 
				
			||||||
                delete history[noteId];
 | 
					        delete history[noteId]
 | 
				
			||||||
        setHistory(req.user.id, history, function (err, count) {
 | 
					        setHistory(req.user.id, history, function (err, count) {
 | 
				
			||||||
                    if (err) return response.errorInternalError(res);
 | 
					          if (err) return response.errorInternalError(res)
 | 
				
			||||||
                    res.end();
 | 
					          res.end()
 | 
				
			||||||
                });
 | 
					        })
 | 
				
			||||||
            });
 | 
					      })
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
        return response.errorForbidden(res);
 | 
					    return response.errorForbidden(res)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
module.exports = History;
 | 
					module.exports = History
 | 
				
			||||||
 | 
				
			|||||||
@ -1,25 +1,23 @@
 | 
				
			|||||||
"use strict";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// external modules
 | 
					// external modules
 | 
				
			||||||
var randomcolor = require('randomcolor');
 | 
					var randomcolor = require('randomcolor')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// core
 | 
					// core
 | 
				
			||||||
module.exports = function(name) {
 | 
					module.exports = function (name) {
 | 
				
			||||||
  var color = randomcolor({
 | 
					  var color = randomcolor({
 | 
				
			||||||
    seed: name,
 | 
					    seed: name,
 | 
				
			||||||
    luminosity: 'dark'
 | 
					    luminosity: 'dark'
 | 
				
			||||||
    });
 | 
					  })
 | 
				
			||||||
    var letter = name.substring(0, 1).toUpperCase();
 | 
					  var letter = name.substring(0, 1).toUpperCase()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    var svg = '<?xml version="1.0" encoding="UTF-8" standalone="no"?>';
 | 
					  var svg = '<?xml version="1.0" encoding="UTF-8" standalone="no"?>'
 | 
				
			||||||
    svg += '<svg xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" height="96" width="96" version="1.1" viewBox="0 0 96 96">';
 | 
					  svg += '<svg xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" height="96" width="96" version="1.1" viewBox="0 0 96 96">'
 | 
				
			||||||
    svg += '<g>';
 | 
					  svg += '<g>'
 | 
				
			||||||
    svg += '<rect width="96" height="96" fill="' + color + '" />';
 | 
					  svg += '<rect width="96" height="96" fill="' + color + '" />'
 | 
				
			||||||
    svg += '<text font-size="64px" font-family="sans-serif" text-anchor="middle" fill="#ffffff">';
 | 
					  svg += '<text font-size="64px" font-family="sans-serif" text-anchor="middle" fill="#ffffff">'
 | 
				
			||||||
    svg += '<tspan x="48" y="72" stroke-width=".26458px" fill="#ffffff">' + letter + '</tspan>';
 | 
					  svg += '<tspan x="48" y="72" stroke-width=".26458px" fill="#ffffff">' + letter + '</tspan>'
 | 
				
			||||||
    svg += '</text>';
 | 
					  svg += '</text>'
 | 
				
			||||||
    svg += '</g>';
 | 
					  svg += '</g>'
 | 
				
			||||||
    svg += '</svg>';
 | 
					  svg += '</svg>'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return 'data:image/svg+xml;base64,' + new Buffer(svg).toString('base64');
 | 
					  return 'data:image/svg+xml;base64,' + new Buffer(svg).toString('base64')
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,5 @@
 | 
				
			|||||||
var winston = require('winston');
 | 
					var winston = require('winston')
 | 
				
			||||||
winston.emitErrs = true;
 | 
					winston.emitErrs = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var logger = new winston.Logger({
 | 
					var logger = new winston.Logger({
 | 
				
			||||||
  transports: [
 | 
					  transports: [
 | 
				
			||||||
@ -12,11 +12,11 @@ var logger = new winston.Logger({
 | 
				
			|||||||
    })
 | 
					    })
 | 
				
			||||||
  ],
 | 
					  ],
 | 
				
			||||||
  exitOnError: false
 | 
					  exitOnError: false
 | 
				
			||||||
});
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
module.exports = logger;
 | 
					module.exports = logger
 | 
				
			||||||
module.exports.stream = {
 | 
					module.exports.stream = {
 | 
				
			||||||
    write: function(message, encoding){
 | 
					  write: function (message, encoding) {
 | 
				
			||||||
        logger.info(message);
 | 
					    logger.info(message)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,15 +1,11 @@
 | 
				
			|||||||
"use strict";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
module.exports = {
 | 
					module.exports = {
 | 
				
			||||||
  up: function (queryInterface, Sequelize) {
 | 
					  up: function (queryInterface, Sequelize) {
 | 
				
			||||||
        queryInterface.addColumn('Users', 'accessToken', Sequelize.STRING);
 | 
					    queryInterface.addColumn('Users', 'accessToken', Sequelize.STRING)
 | 
				
			||||||
        queryInterface.addColumn('Users', 'refreshToken', Sequelize.STRING);
 | 
					    queryInterface.addColumn('Users', 'refreshToken', Sequelize.STRING)
 | 
				
			||||||
        return;
 | 
					 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  down: function (queryInterface, Sequelize) {
 | 
					  down: function (queryInterface, Sequelize) {
 | 
				
			||||||
        queryInterface.removeColumn('Users', 'accessToken');
 | 
					    queryInterface.removeColumn('Users', 'accessToken')
 | 
				
			||||||
        queryInterface.removeColumn('Users', 'refreshToken');
 | 
					    queryInterface.removeColumn('Users', 'refreshToken')
 | 
				
			||||||
        return;
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,8 +1,6 @@
 | 
				
			|||||||
'use strict';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
module.exports = {
 | 
					module.exports = {
 | 
				
			||||||
  up: function (queryInterface, Sequelize) {
 | 
					  up: function (queryInterface, Sequelize) {
 | 
				
			||||||
    queryInterface.addColumn('Notes', 'savedAt', Sequelize.DATE);
 | 
					    queryInterface.addColumn('Notes', 'savedAt', Sequelize.DATE)
 | 
				
			||||||
    queryInterface.createTable('Revisions', {
 | 
					    queryInterface.createTable('Revisions', {
 | 
				
			||||||
      id: {
 | 
					      id: {
 | 
				
			||||||
        type: Sequelize.UUID,
 | 
					        type: Sequelize.UUID,
 | 
				
			||||||
@ -15,13 +13,11 @@ module.exports = {
 | 
				
			|||||||
      length: Sequelize.INTEGER,
 | 
					      length: Sequelize.INTEGER,
 | 
				
			||||||
      createdAt: Sequelize.DATE,
 | 
					      createdAt: Sequelize.DATE,
 | 
				
			||||||
      updatedAt: Sequelize.DATE
 | 
					      updatedAt: Sequelize.DATE
 | 
				
			||||||
    });
 | 
					    })
 | 
				
			||||||
    return;
 | 
					 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  down: function (queryInterface, Sequelize) {
 | 
					  down: function (queryInterface, Sequelize) {
 | 
				
			||||||
    queryInterface.dropTable('Revisions');
 | 
					    queryInterface.dropTable('Revisions')
 | 
				
			||||||
    queryInterface.removeColumn('Notes', 'savedAt');
 | 
					    queryInterface.removeColumn('Notes', 'savedAt')
 | 
				
			||||||
    return;
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,9 +1,7 @@
 | 
				
			|||||||
'use strict';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
module.exports = {
 | 
					module.exports = {
 | 
				
			||||||
  up: function (queryInterface, Sequelize) {
 | 
					  up: function (queryInterface, Sequelize) {
 | 
				
			||||||
    queryInterface.addColumn('Notes', 'authorship', Sequelize.TEXT);
 | 
					    queryInterface.addColumn('Notes', 'authorship', Sequelize.TEXT)
 | 
				
			||||||
    queryInterface.addColumn('Revisions', 'authorship', Sequelize.TEXT);
 | 
					    queryInterface.addColumn('Revisions', 'authorship', Sequelize.TEXT)
 | 
				
			||||||
    queryInterface.createTable('Authors', {
 | 
					    queryInterface.createTable('Authors', {
 | 
				
			||||||
      id: {
 | 
					      id: {
 | 
				
			||||||
        type: Sequelize.INTEGER,
 | 
					        type: Sequelize.INTEGER,
 | 
				
			||||||
@ -15,14 +13,12 @@ module.exports = {
 | 
				
			|||||||
      userId: Sequelize.UUID,
 | 
					      userId: Sequelize.UUID,
 | 
				
			||||||
      createdAt: Sequelize.DATE,
 | 
					      createdAt: Sequelize.DATE,
 | 
				
			||||||
      updatedAt: Sequelize.DATE
 | 
					      updatedAt: Sequelize.DATE
 | 
				
			||||||
    });
 | 
					    })
 | 
				
			||||||
    return;
 | 
					 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  down: function (queryInterface, Sequelize) {
 | 
					  down: function (queryInterface, Sequelize) {
 | 
				
			||||||
    queryInterface.dropTable('Authors');
 | 
					    queryInterface.dropTable('Authors')
 | 
				
			||||||
    queryInterface.removeColumn('Revisions', 'authorship');
 | 
					    queryInterface.removeColumn('Revisions', 'authorship')
 | 
				
			||||||
    queryInterface.removeColumn('Notes', 'authorship');
 | 
					    queryInterface.removeColumn('Notes', 'authorship')
 | 
				
			||||||
    return;
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,11 +1,9 @@
 | 
				
			|||||||
'use strict';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
module.exports = {
 | 
					module.exports = {
 | 
				
			||||||
  up: function (queryInterface, Sequelize) {
 | 
					  up: function (queryInterface, Sequelize) {
 | 
				
			||||||
    queryInterface.addColumn('Notes', 'deletedAt', Sequelize.DATE);
 | 
					    queryInterface.addColumn('Notes', 'deletedAt', Sequelize.DATE)
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  down: function (queryInterface, Sequelize) {
 | 
					  down: function (queryInterface, Sequelize) {
 | 
				
			||||||
    queryInterface.removeColumn('Notes', 'deletedAt');
 | 
					    queryInterface.removeColumn('Notes', 'deletedAt')
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,13 +1,11 @@
 | 
				
			|||||||
'use strict';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
module.exports = {
 | 
					module.exports = {
 | 
				
			||||||
  up: function (queryInterface, Sequelize) {
 | 
					  up: function (queryInterface, Sequelize) {
 | 
				
			||||||
    queryInterface.addColumn('Users', 'email', Sequelize.TEXT);
 | 
					    queryInterface.addColumn('Users', 'email', Sequelize.TEXT)
 | 
				
			||||||
    queryInterface.addColumn('Users', 'password', Sequelize.TEXT);
 | 
					    queryInterface.addColumn('Users', 'password', Sequelize.TEXT)
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  down: function (queryInterface, Sequelize) {
 | 
					  down: function (queryInterface, Sequelize) {
 | 
				
			||||||
    queryInterface.removeColumn('Users', 'email');
 | 
					    queryInterface.removeColumn('Users', 'email')
 | 
				
			||||||
    queryInterface.removeColumn('Users', 'password');
 | 
					    queryInterface.removeColumn('Users', 'password')
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,13 +1,8 @@
 | 
				
			|||||||
"use strict";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// external modules
 | 
					// external modules
 | 
				
			||||||
var Sequelize = require("sequelize");
 | 
					var Sequelize = require('sequelize')
 | 
				
			||||||
 | 
					 | 
				
			||||||
// core
 | 
					 | 
				
			||||||
var logger = require("../logger.js");
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
module.exports = function (sequelize, DataTypes) {
 | 
					module.exports = function (sequelize, DataTypes) {
 | 
				
			||||||
    var Author = sequelize.define("Author", {
 | 
					  var Author = sequelize.define('Author', {
 | 
				
			||||||
    id: {
 | 
					    id: {
 | 
				
			||||||
      type: Sequelize.INTEGER,
 | 
					      type: Sequelize.INTEGER,
 | 
				
			||||||
      primaryKey: true,
 | 
					      primaryKey: true,
 | 
				
			||||||
@ -26,18 +21,17 @@ module.exports = function (sequelize, DataTypes) {
 | 
				
			|||||||
    classMethods: {
 | 
					    classMethods: {
 | 
				
			||||||
      associate: function (models) {
 | 
					      associate: function (models) {
 | 
				
			||||||
        Author.belongsTo(models.Note, {
 | 
					        Author.belongsTo(models.Note, {
 | 
				
			||||||
                    foreignKey: "noteId",
 | 
					          foreignKey: 'noteId',
 | 
				
			||||||
                    as: "note",
 | 
					          as: 'note',
 | 
				
			||||||
          constraints: false
 | 
					          constraints: false
 | 
				
			||||||
                });
 | 
					        })
 | 
				
			||||||
        Author.belongsTo(models.User, {
 | 
					        Author.belongsTo(models.User, {
 | 
				
			||||||
                    foreignKey: "userId",
 | 
					          foreignKey: 'userId',
 | 
				
			||||||
                    as: "user",
 | 
					          as: 'user',
 | 
				
			||||||
          constraints: false
 | 
					          constraints: false
 | 
				
			||||||
                });
 | 
					        })
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    });
 | 
					  })
 | 
				
			||||||
    
 | 
					  return Author
 | 
				
			||||||
    return Author;
 | 
					}
 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
				
			|||||||
@ -1,57 +1,55 @@
 | 
				
			|||||||
"use strict";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// external modules
 | 
					// external modules
 | 
				
			||||||
var fs = require("fs");
 | 
					var fs = require('fs')
 | 
				
			||||||
var path = require("path");
 | 
					var path = require('path')
 | 
				
			||||||
var Sequelize = require("sequelize");
 | 
					var Sequelize = require('sequelize')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// core
 | 
					// core
 | 
				
			||||||
var config = require('../config.js');
 | 
					var config = require('../config.js')
 | 
				
			||||||
var logger = require("../logger.js");
 | 
					var logger = require('../logger.js')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var dbconfig = config.db;
 | 
					var dbconfig = config.db
 | 
				
			||||||
dbconfig.logging = config.debug ? logger.info : false;
 | 
					dbconfig.logging = config.debug ? logger.info : false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var sequelize = null;
 | 
					var sequelize = null
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Heroku specific
 | 
					// Heroku specific
 | 
				
			||||||
if (config.dburl)
 | 
					if (config.dburl) {
 | 
				
			||||||
    sequelize = new Sequelize(config.dburl, dbconfig);
 | 
					  sequelize = new Sequelize(config.dburl, dbconfig)
 | 
				
			||||||
else
 | 
					} else {
 | 
				
			||||||
    sequelize = new Sequelize(dbconfig.database, dbconfig.username, dbconfig.password, dbconfig);
 | 
					  sequelize = new Sequelize(dbconfig.database, dbconfig.username, dbconfig.password, dbconfig)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// [Postgres] Handling NULL bytes
 | 
					// [Postgres] Handling NULL bytes
 | 
				
			||||||
// https://github.com/sequelize/sequelize/issues/6485
 | 
					// https://github.com/sequelize/sequelize/issues/6485
 | 
				
			||||||
function stripNullByte(value) {
 | 
					function stripNullByte (value) {
 | 
				
			||||||
    return value ? value.replace(/\u0000/g, "") : value;
 | 
					  return value ? value.replace(/\u0000/g, '') : value
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
sequelize.stripNullByte = stripNullByte;
 | 
					sequelize.stripNullByte = stripNullByte
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function processData(data, _default, process) {
 | 
					function processData (data, _default, process) {
 | 
				
			||||||
    if (data === undefined) return data;
 | 
					  if (data === undefined) return data
 | 
				
			||||||
    else return data === null ? _default : (process ? process(data) : data);
 | 
					  else return data === null ? _default : (process ? process(data) : data)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
sequelize.processData = processData;
 | 
					sequelize.processData = processData
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var db = {};
 | 
					var db = {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fs
 | 
					fs.readdirSync(__dirname)
 | 
				
			||||||
    .readdirSync(__dirname)
 | 
					 | 
				
			||||||
    .filter(function (file) {
 | 
					    .filter(function (file) {
 | 
				
			||||||
        return (file.indexOf(".") !== 0) && (file !== "index.js");
 | 
					      return (file.indexOf('.') !== 0) && (file !== 'index.js')
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
    .forEach(function (file) {
 | 
					    .forEach(function (file) {
 | 
				
			||||||
        var model = sequelize.import(path.join(__dirname, file));
 | 
					      var model = sequelize.import(path.join(__dirname, file))
 | 
				
			||||||
        db[model.name] = model;
 | 
					      db[model.name] = model
 | 
				
			||||||
    });
 | 
					    })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Object.keys(db).forEach(function (modelName) {
 | 
					Object.keys(db).forEach(function (modelName) {
 | 
				
			||||||
    if ("associate" in db[modelName]) {
 | 
					  if ('associate' in db[modelName]) {
 | 
				
			||||||
        db[modelName].associate(db);
 | 
					    db[modelName].associate(db)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
});
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
db.sequelize = sequelize;
 | 
					db.sequelize = sequelize
 | 
				
			||||||
db.Sequelize = Sequelize;
 | 
					db.Sequelize = Sequelize
 | 
				
			||||||
 | 
					
 | 
				
			||||||
module.exports = db;
 | 
					module.exports = db
 | 
				
			||||||
 | 
				
			|||||||
@ -1,32 +1,30 @@
 | 
				
			|||||||
"use strict";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// external modules
 | 
					// external modules
 | 
				
			||||||
var fs = require('fs');
 | 
					var fs = require('fs')
 | 
				
			||||||
var path = require('path');
 | 
					var path = require('path')
 | 
				
			||||||
var LZString = require('lz-string');
 | 
					var LZString = require('lz-string')
 | 
				
			||||||
var md = require('markdown-it')();
 | 
					var md = require('markdown-it')()
 | 
				
			||||||
var metaMarked = require('meta-marked');
 | 
					var metaMarked = require('meta-marked')
 | 
				
			||||||
var cheerio = require('cheerio');
 | 
					var cheerio = require('cheerio')
 | 
				
			||||||
var shortId = require('shortid');
 | 
					var shortId = require('shortid')
 | 
				
			||||||
var Sequelize = require("sequelize");
 | 
					var Sequelize = require('sequelize')
 | 
				
			||||||
var async = require('async');
 | 
					var async = require('async')
 | 
				
			||||||
var moment = require('moment');
 | 
					var moment = require('moment')
 | 
				
			||||||
var DiffMatchPatch = require('diff-match-patch');
 | 
					var DiffMatchPatch = require('diff-match-patch')
 | 
				
			||||||
var dmp = new DiffMatchPatch();
 | 
					var dmp = new DiffMatchPatch()
 | 
				
			||||||
var S = require('string');
 | 
					var S = require('string')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// core
 | 
					// core
 | 
				
			||||||
var config = require("../config.js");
 | 
					var config = require('../config.js')
 | 
				
			||||||
var logger = require("../logger.js");
 | 
					var logger = require('../logger.js')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//ot
 | 
					// ot
 | 
				
			||||||
var ot = require("../ot/index.js");
 | 
					var ot = require('../ot/index.js')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// permission types
 | 
					// permission types
 | 
				
			||||||
var permissionTypes = ["freely", "editable", "limited", "locked", "protected", "private"];
 | 
					var permissionTypes = ['freely', 'editable', 'limited', 'locked', 'protected', 'private']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
module.exports = function (sequelize, DataTypes) {
 | 
					module.exports = function (sequelize, DataTypes) {
 | 
				
			||||||
    var Note = sequelize.define("Note", {
 | 
					  var Note = sequelize.define('Note', {
 | 
				
			||||||
    id: {
 | 
					    id: {
 | 
				
			||||||
      type: DataTypes.UUID,
 | 
					      type: DataTypes.UUID,
 | 
				
			||||||
      primaryKey: true,
 | 
					      primaryKey: true,
 | 
				
			||||||
@ -54,28 +52,28 @@ module.exports = function (sequelize, DataTypes) {
 | 
				
			|||||||
    title: {
 | 
					    title: {
 | 
				
			||||||
      type: DataTypes.TEXT,
 | 
					      type: DataTypes.TEXT,
 | 
				
			||||||
      get: function () {
 | 
					      get: function () {
 | 
				
			||||||
                return sequelize.processData(this.getDataValue('title'), "");
 | 
					        return sequelize.processData(this.getDataValue('title'), '')
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      set: function (value) {
 | 
					      set: function (value) {
 | 
				
			||||||
                this.setDataValue('title', sequelize.stripNullByte(value));
 | 
					        this.setDataValue('title', sequelize.stripNullByte(value))
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    content: {
 | 
					    content: {
 | 
				
			||||||
      type: DataTypes.TEXT,
 | 
					      type: DataTypes.TEXT,
 | 
				
			||||||
      get: function () {
 | 
					      get: function () {
 | 
				
			||||||
                return sequelize.processData(this.getDataValue('content'), "");
 | 
					        return sequelize.processData(this.getDataValue('content'), '')
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      set: function (value) {
 | 
					      set: function (value) {
 | 
				
			||||||
                this.setDataValue('content', sequelize.stripNullByte(value));
 | 
					        this.setDataValue('content', sequelize.stripNullByte(value))
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    authorship: {
 | 
					    authorship: {
 | 
				
			||||||
      type: DataTypes.TEXT,
 | 
					      type: DataTypes.TEXT,
 | 
				
			||||||
      get: function () {
 | 
					      get: function () {
 | 
				
			||||||
                return sequelize.processData(this.getDataValue('authorship'), [], JSON.parse);
 | 
					        return sequelize.processData(this.getDataValue('authorship'), [], JSON.parse)
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      set: function (value) {
 | 
					      set: function (value) {
 | 
				
			||||||
                this.setDataValue('authorship', JSON.stringify(value));
 | 
					        this.setDataValue('authorship', JSON.stringify(value))
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    lastchangeAt: {
 | 
					    lastchangeAt: {
 | 
				
			||||||
@ -89,39 +87,36 @@ module.exports = function (sequelize, DataTypes) {
 | 
				
			|||||||
    classMethods: {
 | 
					    classMethods: {
 | 
				
			||||||
      associate: function (models) {
 | 
					      associate: function (models) {
 | 
				
			||||||
        Note.belongsTo(models.User, {
 | 
					        Note.belongsTo(models.User, {
 | 
				
			||||||
                    foreignKey: "ownerId",
 | 
					          foreignKey: 'ownerId',
 | 
				
			||||||
                    as: "owner",
 | 
					          as: 'owner',
 | 
				
			||||||
          constraints: false
 | 
					          constraints: false
 | 
				
			||||||
                });
 | 
					        })
 | 
				
			||||||
        Note.belongsTo(models.User, {
 | 
					        Note.belongsTo(models.User, {
 | 
				
			||||||
                    foreignKey: "lastchangeuserId",
 | 
					          foreignKey: 'lastchangeuserId',
 | 
				
			||||||
                    as: "lastchangeuser",
 | 
					          as: 'lastchangeuser',
 | 
				
			||||||
          constraints: false
 | 
					          constraints: false
 | 
				
			||||||
                });
 | 
					        })
 | 
				
			||||||
        Note.hasMany(models.Revision, {
 | 
					        Note.hasMany(models.Revision, {
 | 
				
			||||||
                    foreignKey: "noteId",
 | 
					          foreignKey: 'noteId',
 | 
				
			||||||
          constraints: false
 | 
					          constraints: false
 | 
				
			||||||
                });
 | 
					        })
 | 
				
			||||||
        Note.hasMany(models.Author, {
 | 
					        Note.hasMany(models.Author, {
 | 
				
			||||||
                    foreignKey: "noteId",
 | 
					          foreignKey: 'noteId',
 | 
				
			||||||
                    as: "authors",
 | 
					          as: 'authors',
 | 
				
			||||||
          constraints: false
 | 
					          constraints: false
 | 
				
			||||||
                });
 | 
					        })
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      checkFileExist: function (filePath) {
 | 
					      checkFileExist: function (filePath) {
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
                    return fs.statSync(filePath).isFile();
 | 
					          return fs.statSync(filePath).isFile()
 | 
				
			||||||
        } catch (err) {
 | 
					        } catch (err) {
 | 
				
			||||||
                    return false;
 | 
					          return false
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      checkNoteIdValid: function (id) {
 | 
					      checkNoteIdValid: function (id) {
 | 
				
			||||||
                var uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
 | 
					        var uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i
 | 
				
			||||||
                var result = id.match(uuidRegex);
 | 
					        var result = id.match(uuidRegex)
 | 
				
			||||||
                if (result && result.length == 1)
 | 
					        if (result && result.length === 1) { return true } else { return false }
 | 
				
			||||||
                    return true;
 | 
					 | 
				
			||||||
                else
 | 
					 | 
				
			||||||
                    return false;
 | 
					 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      parseNoteId: function (noteId, callback) {
 | 
					      parseNoteId: function (noteId, callback) {
 | 
				
			||||||
        async.series({
 | 
					        async.series({
 | 
				
			||||||
@ -133,15 +128,15 @@ module.exports = function (sequelize, DataTypes) {
 | 
				
			|||||||
              }
 | 
					              }
 | 
				
			||||||
            }).then(function (note) {
 | 
					            }).then(function (note) {
 | 
				
			||||||
              if (note) {
 | 
					              if (note) {
 | 
				
			||||||
                                var filePath = path.join(config.docspath, noteId + '.md');
 | 
					                let filePath = path.join(config.docspath, noteId + '.md')
 | 
				
			||||||
                if (Note.checkFileExist(filePath)) {
 | 
					                if (Note.checkFileExist(filePath)) {
 | 
				
			||||||
                  // if doc in filesystem have newer modified time than last change time
 | 
					                  // if doc in filesystem have newer modified time than last change time
 | 
				
			||||||
                  // then will update the doc in db
 | 
					                  // then will update the doc in db
 | 
				
			||||||
                                    var fsModifiedTime = moment(fs.statSync(filePath).mtime);
 | 
					                  var fsModifiedTime = moment(fs.statSync(filePath).mtime)
 | 
				
			||||||
                                    var dbModifiedTime = moment(note.lastchangeAt || note.createdAt);
 | 
					                  var dbModifiedTime = moment(note.lastchangeAt || note.createdAt)
 | 
				
			||||||
                                    var body = fs.readFileSync(filePath, 'utf8');
 | 
					                  var body = fs.readFileSync(filePath, 'utf8')
 | 
				
			||||||
                                    var contentLength = body.length;
 | 
					                  var contentLength = body.length
 | 
				
			||||||
                                    var title = Note.parseNoteTitle(body);
 | 
					                  var title = Note.parseNoteTitle(body)
 | 
				
			||||||
                  if (fsModifiedTime.isAfter(dbModifiedTime) && note.content !== body) {
 | 
					                  if (fsModifiedTime.isAfter(dbModifiedTime) && note.content !== body) {
 | 
				
			||||||
                    note.update({
 | 
					                    note.update({
 | 
				
			||||||
                      title: title,
 | 
					                      title: title,
 | 
				
			||||||
@ -149,61 +144,58 @@ module.exports = function (sequelize, DataTypes) {
 | 
				
			|||||||
                      lastchangeAt: fsModifiedTime
 | 
					                      lastchangeAt: fsModifiedTime
 | 
				
			||||||
                    }).then(function (note) {
 | 
					                    }).then(function (note) {
 | 
				
			||||||
                      sequelize.models.Revision.saveNoteRevision(note, function (err, revision) {
 | 
					                      sequelize.models.Revision.saveNoteRevision(note, function (err, revision) {
 | 
				
			||||||
                                                if (err) return _callback(err, null);
 | 
					                        if (err) return _callback(err, null)
 | 
				
			||||||
                        // update authorship on after making revision of docs
 | 
					                        // update authorship on after making revision of docs
 | 
				
			||||||
                                                var patch = dmp.patch_fromText(revision.patch);
 | 
					                        var patch = dmp.patch_fromText(revision.patch)
 | 
				
			||||||
                                                var operations = Note.transformPatchToOperations(patch, contentLength);
 | 
					                        var operations = Note.transformPatchToOperations(patch, contentLength)
 | 
				
			||||||
                                                var authorship = note.authorship;
 | 
					                        var authorship = note.authorship
 | 
				
			||||||
                                                for (var i = 0; i < operations.length; i++) {
 | 
					                        for (let i = 0; i < operations.length; i++) {
 | 
				
			||||||
                                                    authorship = Note.updateAuthorshipByOperation(operations[i], null, authorship);
 | 
					                          authorship = Note.updateAuthorshipByOperation(operations[i], null, authorship)
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                        note.update({
 | 
					                        note.update({
 | 
				
			||||||
                          authorship: JSON.stringify(authorship)
 | 
					                          authorship: JSON.stringify(authorship)
 | 
				
			||||||
                        }).then(function (note) {
 | 
					                        }).then(function (note) {
 | 
				
			||||||
                                                    return callback(null, note.id);
 | 
					                          return callback(null, note.id)
 | 
				
			||||||
                        }).catch(function (err) {
 | 
					                        }).catch(function (err) {
 | 
				
			||||||
                                                    return _callback(err, null);
 | 
					                          return _callback(err, null)
 | 
				
			||||||
                                                });
 | 
					                        })
 | 
				
			||||||
                                            });
 | 
					                      })
 | 
				
			||||||
                    }).catch(function (err) {
 | 
					                    }).catch(function (err) {
 | 
				
			||||||
                                            return _callback(err, null);
 | 
					                      return _callback(err, null)
 | 
				
			||||||
                                        });
 | 
					                    })
 | 
				
			||||||
                  } else {
 | 
					                  } else {
 | 
				
			||||||
                                        return callback(null, note.id);
 | 
					                    return callback(null, note.id)
 | 
				
			||||||
                  }
 | 
					                  }
 | 
				
			||||||
                } else {
 | 
					                } else {
 | 
				
			||||||
                                    return callback(null, note.id);
 | 
					                  return callback(null, note.id)
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
              } else {
 | 
					              } else {
 | 
				
			||||||
                                var filePath = path.join(config.docspath, noteId + '.md');
 | 
					                var filePath = path.join(config.docspath, noteId + '.md')
 | 
				
			||||||
                if (Note.checkFileExist(filePath)) {
 | 
					                if (Note.checkFileExist(filePath)) {
 | 
				
			||||||
                  Note.create({
 | 
					                  Note.create({
 | 
				
			||||||
                    alias: noteId,
 | 
					                    alias: noteId,
 | 
				
			||||||
                    owner: null,
 | 
					                    owner: null,
 | 
				
			||||||
                    permission: 'locked'
 | 
					                    permission: 'locked'
 | 
				
			||||||
                  }).then(function (note) {
 | 
					                  }).then(function (note) {
 | 
				
			||||||
                                        return callback(null, note.id);
 | 
					                    return callback(null, note.id)
 | 
				
			||||||
                  }).catch(function (err) {
 | 
					                  }).catch(function (err) {
 | 
				
			||||||
                                        return _callback(err, null);
 | 
					                    return _callback(err, null)
 | 
				
			||||||
                                    });
 | 
					                  })
 | 
				
			||||||
                } else {
 | 
					                } else {
 | 
				
			||||||
                                    return _callback(null, null);
 | 
					                  return _callback(null, null)
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
              }
 | 
					              }
 | 
				
			||||||
            }).catch(function (err) {
 | 
					            }).catch(function (err) {
 | 
				
			||||||
                            return _callback(err, null);
 | 
					              return _callback(err, null)
 | 
				
			||||||
                        });
 | 
					            })
 | 
				
			||||||
          },
 | 
					          },
 | 
				
			||||||
          parseNoteIdByLZString: function (_callback) {
 | 
					          parseNoteIdByLZString: function (_callback) {
 | 
				
			||||||
            // try to parse note id by LZString Base64
 | 
					            // try to parse note id by LZString Base64
 | 
				
			||||||
            try {
 | 
					            try {
 | 
				
			||||||
                            var id = LZString.decompressFromBase64(noteId);
 | 
					              var id = LZString.decompressFromBase64(noteId)
 | 
				
			||||||
                            if (id && Note.checkNoteIdValid(id))
 | 
					              if (id && Note.checkNoteIdValid(id)) { return callback(null, id) } else { return _callback(null, null) }
 | 
				
			||||||
                                return callback(null, id);
 | 
					 | 
				
			||||||
                            else
 | 
					 | 
				
			||||||
                                return _callback(null, null);
 | 
					 | 
				
			||||||
            } catch (err) {
 | 
					            } catch (err) {
 | 
				
			||||||
                            return _callback(err, null);
 | 
					              return _callback(err, null)
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
          },
 | 
					          },
 | 
				
			||||||
          parseNoteIdByShortId: function (_callback) {
 | 
					          parseNoteIdByShortId: function (_callback) {
 | 
				
			||||||
@ -215,321 +207,318 @@ module.exports = function (sequelize, DataTypes) {
 | 
				
			|||||||
                    shortid: noteId
 | 
					                    shortid: noteId
 | 
				
			||||||
                  }
 | 
					                  }
 | 
				
			||||||
                }).then(function (note) {
 | 
					                }).then(function (note) {
 | 
				
			||||||
                                    if (!note) return _callback(null, null);
 | 
					                  if (!note) return _callback(null, null)
 | 
				
			||||||
                                    return callback(null, note.id);
 | 
					                  return callback(null, note.id)
 | 
				
			||||||
                }).catch(function (err) {
 | 
					                }).catch(function (err) {
 | 
				
			||||||
                                    return _callback(err, null);
 | 
					                  return _callback(err, null)
 | 
				
			||||||
                                });
 | 
					                })
 | 
				
			||||||
              } else {
 | 
					              } else {
 | 
				
			||||||
                                return _callback(null, null);
 | 
					                return _callback(null, null)
 | 
				
			||||||
              }
 | 
					              }
 | 
				
			||||||
            } catch (err) {
 | 
					            } catch (err) {
 | 
				
			||||||
                            return _callback(err, null);
 | 
					              return _callback(err, null)
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        }, function (err, result) {
 | 
					        }, function (err, result) {
 | 
				
			||||||
          if (err) {
 | 
					          if (err) {
 | 
				
			||||||
                        logger.error(err);
 | 
					            logger.error(err)
 | 
				
			||||||
                        return callback(err, null);
 | 
					            return callback(err, null)
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
                    return callback(null, null);
 | 
					          return callback(null, null)
 | 
				
			||||||
                });
 | 
					        })
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      parseNoteInfo: function (body) {
 | 
					      parseNoteInfo: function (body) {
 | 
				
			||||||
                var parsed = Note.extractMeta(body);
 | 
					        var parsed = Note.extractMeta(body)
 | 
				
			||||||
                var $ = cheerio.load(md.render(parsed.markdown));
 | 
					        var $ = cheerio.load(md.render(parsed.markdown))
 | 
				
			||||||
        return {
 | 
					        return {
 | 
				
			||||||
          title: Note.extractNoteTitle(parsed.meta, $),
 | 
					          title: Note.extractNoteTitle(parsed.meta, $),
 | 
				
			||||||
          tags: Note.extractNoteTags(parsed.meta, $)
 | 
					          tags: Note.extractNoteTags(parsed.meta, $)
 | 
				
			||||||
                };
 | 
					        }
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      parseNoteTitle: function (body) {
 | 
					      parseNoteTitle: function (body) {
 | 
				
			||||||
                var parsed = Note.extractMeta(body);
 | 
					        var parsed = Note.extractMeta(body)
 | 
				
			||||||
                var $ = cheerio.load(md.render(parsed.markdown));
 | 
					        var $ = cheerio.load(md.render(parsed.markdown))
 | 
				
			||||||
                return Note.extractNoteTitle(parsed.meta, $);
 | 
					        return Note.extractNoteTitle(parsed.meta, $)
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      extractNoteTitle: function (meta, $) {
 | 
					      extractNoteTitle: function (meta, $) {
 | 
				
			||||||
                var title = "";
 | 
					        var title = ''
 | 
				
			||||||
                if (meta.title && (typeof meta.title == "string" || typeof meta.title == "number")) {
 | 
					        if (meta.title && (typeof meta.title === 'string' || typeof meta.title === 'number')) {
 | 
				
			||||||
                    title = meta.title;
 | 
					          title = meta.title
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
                    var h1s = $("h1");
 | 
					          var h1s = $('h1')
 | 
				
			||||||
                    if (h1s.length > 0 && h1s.first().text().split('\n').length == 1)
 | 
					          if (h1s.length > 0 && h1s.first().text().split('\n').length === 1) { title = S(h1s.first().text()).stripTags().s }
 | 
				
			||||||
                        title = S(h1s.first().text()).stripTags().s;
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
                if (!title) title = "Untitled";
 | 
					        if (!title) title = 'Untitled'
 | 
				
			||||||
                return title;
 | 
					        return title
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      generateDescription: function (markdown) {
 | 
					      generateDescription: function (markdown) {
 | 
				
			||||||
                return markdown.substr(0, 100).replace(/(?:\r\n|\r|\n)/g, ' ');
 | 
					        return markdown.substr(0, 100).replace(/(?:\r\n|\r|\n)/g, ' ')
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      decodeTitle: function (title) {
 | 
					      decodeTitle: function (title) {
 | 
				
			||||||
                return title ? title : 'Untitled';
 | 
					        return title || 'Untitled'
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      generateWebTitle: function (title) {
 | 
					      generateWebTitle: function (title) {
 | 
				
			||||||
                title = !title || title == "Untitled" ? "HackMD - Collaborative markdown notes" : title + " - HackMD";
 | 
					        title = !title || title === 'Untitled' ? 'HackMD - Collaborative markdown notes' : title + ' - HackMD'
 | 
				
			||||||
                return title;
 | 
					        return title
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      extractNoteTags: function (meta, $) {
 | 
					      extractNoteTags: function (meta, $) {
 | 
				
			||||||
                var tags = [];
 | 
					        var tags = []
 | 
				
			||||||
                var rawtags = [];
 | 
					        var rawtags = []
 | 
				
			||||||
                if (meta.tags && (typeof meta.tags == "string" || typeof meta.tags == "number")) {
 | 
					        if (meta.tags && (typeof meta.tags === 'string' || typeof meta.tags === 'number')) {
 | 
				
			||||||
                    var metaTags = ('' + meta.tags).split(',');
 | 
					          var metaTags = ('' + meta.tags).split(',')
 | 
				
			||||||
                    for (var i = 0; i < metaTags.length; i++) {
 | 
					          for (let i = 0; i < metaTags.length; i++) {
 | 
				
			||||||
                        var text = metaTags[i].trim();
 | 
					            var text = metaTags[i].trim()
 | 
				
			||||||
                        if (text) rawtags.push(text);
 | 
					            if (text) rawtags.push(text)
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
                    var h6s = $("h6");
 | 
					          var h6s = $('h6')
 | 
				
			||||||
          h6s.each(function (key, value) {
 | 
					          h6s.each(function (key, value) {
 | 
				
			||||||
            if (/^tags/gmi.test($(value).text())) {
 | 
					            if (/^tags/gmi.test($(value).text())) {
 | 
				
			||||||
                            var codes = $(value).find("code");
 | 
					              var codes = $(value).find('code')
 | 
				
			||||||
                            for (var i = 0; i < codes.length; i++) {
 | 
					              for (let i = 0; i < codes.length; i++) {
 | 
				
			||||||
                                var text = S($(codes[i]).text().trim()).stripTags().s;
 | 
					                var text = S($(codes[i]).text().trim()).stripTags().s
 | 
				
			||||||
                                if (text) rawtags.push(text);
 | 
					                if (text) rawtags.push(text)
 | 
				
			||||||
              }
 | 
					              }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
                    });
 | 
					          })
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
                for (var i = 0; i < rawtags.length; i++) {
 | 
					        for (let i = 0; i < rawtags.length; i++) {
 | 
				
			||||||
                    var found = false;
 | 
					          var found = false
 | 
				
			||||||
                    for (var j = 0; j < tags.length; j++) {
 | 
					          for (let j = 0; j < tags.length; j++) {
 | 
				
			||||||
                        if (tags[j] == rawtags[i]) {
 | 
					            if (tags[j] === rawtags[i]) {
 | 
				
			||||||
                            found = true;
 | 
					              found = true
 | 
				
			||||||
                            break;
 | 
					              break
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
                    if (!found)
 | 
					          if (!found) { tags.push(rawtags[i]) }
 | 
				
			||||||
                        tags.push(rawtags[i]);
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
                return tags;
 | 
					        return tags
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      extractMeta: function (content) {
 | 
					      extractMeta: function (content) {
 | 
				
			||||||
 | 
					        var obj = null
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
                    var obj = metaMarked(content);
 | 
					          obj = metaMarked(content)
 | 
				
			||||||
                    if (!obj.markdown) obj.markdown = "";
 | 
					          if (!obj.markdown) obj.markdown = ''
 | 
				
			||||||
                    if (!obj.meta) obj.meta = {};
 | 
					          if (!obj.meta) obj.meta = {}
 | 
				
			||||||
        } catch (err) {
 | 
					        } catch (err) {
 | 
				
			||||||
                    var obj = {
 | 
					          obj = {
 | 
				
			||||||
            markdown: content,
 | 
					            markdown: content,
 | 
				
			||||||
            meta: {}
 | 
					            meta: {}
 | 
				
			||||||
                    };
 | 
					 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
                return obj;
 | 
					        }
 | 
				
			||||||
 | 
					        return obj
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      parseMeta: function (meta) {
 | 
					      parseMeta: function (meta) {
 | 
				
			||||||
                var _meta = {};
 | 
					        var _meta = {}
 | 
				
			||||||
        if (meta) {
 | 
					        if (meta) {
 | 
				
			||||||
                    if (meta.title && (typeof meta.title == "string" || typeof meta.title == "number"))
 | 
					          if (meta.title && (typeof meta.title === 'string' || typeof meta.title === 'number')) { _meta.title = meta.title }
 | 
				
			||||||
                        _meta.title = meta.title;
 | 
					          if (meta.description && (typeof meta.description === 'string' || typeof meta.description === 'number')) { _meta.description = meta.description }
 | 
				
			||||||
                    if (meta.description && (typeof meta.description == "string" || typeof meta.description == "number"))
 | 
					          if (meta.robots && (typeof meta.robots === 'string' || typeof meta.robots === 'number')) { _meta.robots = meta.robots }
 | 
				
			||||||
                        _meta.description = meta.description;
 | 
					          if (meta.GA && (typeof meta.GA === 'string' || typeof meta.GA === 'number')) { _meta.GA = meta.GA }
 | 
				
			||||||
                    if (meta.robots && (typeof meta.robots == "string" || typeof meta.robots == "number"))
 | 
					          if (meta.disqus && (typeof meta.disqus === 'string' || typeof meta.disqus === 'number')) { _meta.disqus = meta.disqus }
 | 
				
			||||||
                        _meta.robots = meta.robots;
 | 
					          if (meta.slideOptions && (typeof meta.slideOptions === 'object')) { _meta.slideOptions = meta.slideOptions }
 | 
				
			||||||
                    if (meta.GA && (typeof meta.GA == "string" || typeof meta.GA == "number"))
 | 
					 | 
				
			||||||
                        _meta.GA = meta.GA;
 | 
					 | 
				
			||||||
                    if (meta.disqus && (typeof meta.disqus == "string" || typeof meta.disqus == "number"))
 | 
					 | 
				
			||||||
                        _meta.disqus = meta.disqus;
 | 
					 | 
				
			||||||
                    if (meta.slideOptions && (typeof meta.slideOptions == "object"))
 | 
					 | 
				
			||||||
                        _meta.slideOptions = meta.slideOptions;
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
                return _meta;
 | 
					        return _meta
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      updateAuthorshipByOperation: function (operation, userId, authorships) {
 | 
					      updateAuthorshipByOperation: function (operation, userId, authorships) {
 | 
				
			||||||
                var index = 0;
 | 
					        var index = 0
 | 
				
			||||||
                var timestamp = Date.now();
 | 
					        var timestamp = Date.now()
 | 
				
			||||||
                for (var i = 0; i < operation.length; i++) {
 | 
					        for (let i = 0; i < operation.length; i++) {
 | 
				
			||||||
                    var op = operation[i];
 | 
					          var op = operation[i]
 | 
				
			||||||
          if (ot.TextOperation.isRetain(op)) {
 | 
					          if (ot.TextOperation.isRetain(op)) {
 | 
				
			||||||
                        index += op;
 | 
					            index += op
 | 
				
			||||||
          } else if (ot.TextOperation.isInsert(op)) {
 | 
					          } else if (ot.TextOperation.isInsert(op)) {
 | 
				
			||||||
                        var opStart = index;
 | 
					            let opStart = index
 | 
				
			||||||
                        var opEnd = index + op.length;
 | 
					            let opEnd = index + op.length
 | 
				
			||||||
                        var inserted = false;
 | 
					            var inserted = false
 | 
				
			||||||
            // authorship format: [userId, startPos, endPos, createdAt, updatedAt]
 | 
					            // authorship format: [userId, startPos, endPos, createdAt, updatedAt]
 | 
				
			||||||
                        if (authorships.length <= 0) authorships.push([userId, opStart, opEnd, timestamp, timestamp]);
 | 
					            if (authorships.length <= 0) authorships.push([userId, opStart, opEnd, timestamp, timestamp])
 | 
				
			||||||
            else {
 | 
					            else {
 | 
				
			||||||
                            for (var j = 0; j < authorships.length; j++) {
 | 
					              for (let j = 0; j < authorships.length; j++) {
 | 
				
			||||||
                                var authorship = authorships[j];
 | 
					                let authorship = authorships[j]
 | 
				
			||||||
                if (!inserted) {
 | 
					                if (!inserted) {
 | 
				
			||||||
                                    var nextAuthorship = authorships[j + 1] || -1;
 | 
					                  let nextAuthorship = authorships[j + 1] || -1
 | 
				
			||||||
                                    if (nextAuthorship != -1 && nextAuthorship[1] >= opEnd || j >= authorships.length - 1) {
 | 
					                  if ((nextAuthorship !== -1 && nextAuthorship[1] >= opEnd) || j >= authorships.length - 1) {
 | 
				
			||||||
                    if (authorship[1] < opStart && authorship[2] > opStart) {
 | 
					                    if (authorship[1] < opStart && authorship[2] > opStart) {
 | 
				
			||||||
                      // divide
 | 
					                      // divide
 | 
				
			||||||
                                            var postLength = authorship[2] - opStart;
 | 
					                      let postLength = authorship[2] - opStart
 | 
				
			||||||
                                            authorship[2] = opStart;
 | 
					                      authorship[2] = opStart
 | 
				
			||||||
                                            authorship[4] = timestamp;
 | 
					                      authorship[4] = timestamp
 | 
				
			||||||
                                            authorships.splice(j + 1, 0, [userId, opStart, opEnd, timestamp, timestamp]);
 | 
					                      authorships.splice(j + 1, 0, [userId, opStart, opEnd, timestamp, timestamp])
 | 
				
			||||||
                                            authorships.splice(j + 2, 0, [authorship[0], opEnd, opEnd + postLength, authorship[3], timestamp]);
 | 
					                      authorships.splice(j + 2, 0, [authorship[0], opEnd, opEnd + postLength, authorship[3], timestamp])
 | 
				
			||||||
                                            j += 2;
 | 
					                      j += 2
 | 
				
			||||||
                                            inserted = true;
 | 
					                      inserted = true
 | 
				
			||||||
                    } else if (authorship[1] >= opStart) {
 | 
					                    } else if (authorship[1] >= opStart) {
 | 
				
			||||||
                                            authorships.splice(j, 0, [userId, opStart, opEnd, timestamp, timestamp]);
 | 
					                      authorships.splice(j, 0, [userId, opStart, opEnd, timestamp, timestamp])
 | 
				
			||||||
                                            j += 1;
 | 
					                      j += 1
 | 
				
			||||||
                                            inserted = true;
 | 
					                      inserted = true
 | 
				
			||||||
                    } else if (authorship[2] <= opStart) {
 | 
					                    } else if (authorship[2] <= opStart) {
 | 
				
			||||||
                                            authorships.splice(j + 1, 0, [userId, opStart, opEnd, timestamp, timestamp]);
 | 
					                      authorships.splice(j + 1, 0, [userId, opStart, opEnd, timestamp, timestamp])
 | 
				
			||||||
                                            j += 1;
 | 
					                      j += 1
 | 
				
			||||||
                                            inserted = true;
 | 
					                      inserted = true
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                  }
 | 
					                  }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                if (authorship[1] >= opStart) {
 | 
					                if (authorship[1] >= opStart) {
 | 
				
			||||||
                                    authorship[1] += op.length;
 | 
					                  authorship[1] += op.length
 | 
				
			||||||
                                    authorship[2] += op.length;
 | 
					                  authorship[2] += op.length
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
              }
 | 
					              }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
                        index += op.length;
 | 
					            index += op.length
 | 
				
			||||||
          } else if (ot.TextOperation.isDelete(op)) {
 | 
					          } else if (ot.TextOperation.isDelete(op)) {
 | 
				
			||||||
                        var opStart = index;
 | 
					            let opStart = index
 | 
				
			||||||
                        var opEnd = index - op;
 | 
					            let opEnd = index - op
 | 
				
			||||||
                        if (operation.length == 1) {
 | 
					            if (operation.length === 1) {
 | 
				
			||||||
                            authorships = [];
 | 
					              authorships = []
 | 
				
			||||||
            } else if (authorships.length > 0) {
 | 
					            } else if (authorships.length > 0) {
 | 
				
			||||||
                            for (var j = 0; j < authorships.length; j++) {
 | 
					              for (let j = 0; j < authorships.length; j++) {
 | 
				
			||||||
                                var authorship = authorships[j];
 | 
					                let authorship = authorships[j]
 | 
				
			||||||
                if (authorship[1] >= opStart && authorship[1] <= opEnd && authorship[2] >= opStart && authorship[2] <= opEnd) {
 | 
					                if (authorship[1] >= opStart && authorship[1] <= opEnd && authorship[2] >= opStart && authorship[2] <= opEnd) {
 | 
				
			||||||
                                    authorships.splice(j, 1);
 | 
					                  authorships.splice(j, 1)
 | 
				
			||||||
                                    j -= 1;
 | 
					                  j -= 1
 | 
				
			||||||
                } else if (authorship[1] < opStart && authorship[1] < opEnd && authorship[2] > opStart && authorship[2] > opEnd) {
 | 
					                } else if (authorship[1] < opStart && authorship[1] < opEnd && authorship[2] > opStart && authorship[2] > opEnd) {
 | 
				
			||||||
                                    authorship[2] += op;
 | 
					                  authorship[2] += op
 | 
				
			||||||
                                    authorship[4] = timestamp;
 | 
					                  authorship[4] = timestamp
 | 
				
			||||||
                } else if (authorship[2] >= opStart && authorship[2] <= opEnd) {
 | 
					                } else if (authorship[2] >= opStart && authorship[2] <= opEnd) {
 | 
				
			||||||
                                    authorship[2] = opStart;
 | 
					                  authorship[2] = opStart
 | 
				
			||||||
                                    authorship[4] = timestamp;
 | 
					                  authorship[4] = timestamp
 | 
				
			||||||
                } else if (authorship[1] >= opStart && authorship[1] <= opEnd) {
 | 
					                } else if (authorship[1] >= opStart && authorship[1] <= opEnd) {
 | 
				
			||||||
                                    authorship[1] = opEnd;
 | 
					                  authorship[1] = opEnd
 | 
				
			||||||
                                    authorship[4] = timestamp;
 | 
					                  authorship[4] = timestamp
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                if (authorship[1] >= opEnd) {
 | 
					                if (authorship[1] >= opEnd) {
 | 
				
			||||||
                                    authorship[1] += op;
 | 
					                  authorship[1] += op
 | 
				
			||||||
                                    authorship[2] += op;
 | 
					                  authorship[2] += op
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
              }
 | 
					              }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
                        index += op;
 | 
					            index += op
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        // merge
 | 
					        // merge
 | 
				
			||||||
                for (var j = 0; j < authorships.length; j++) {
 | 
					        for (let j = 0; j < authorships.length; j++) {
 | 
				
			||||||
                    var authorship = authorships[j];
 | 
					          let authorship = authorships[j]
 | 
				
			||||||
                    for (var k = j + 1; k < authorships.length; k++) {
 | 
					          for (let k = j + 1; k < authorships.length; k++) {
 | 
				
			||||||
                        var nextAuthorship = authorships[k];
 | 
					            let nextAuthorship = authorships[k]
 | 
				
			||||||
            if (nextAuthorship && authorship[0] === nextAuthorship[0] && authorship[2] === nextAuthorship[1]) {
 | 
					            if (nextAuthorship && authorship[0] === nextAuthorship[0] && authorship[2] === nextAuthorship[1]) {
 | 
				
			||||||
                            var minTimestamp = Math.min(authorship[3], nextAuthorship[3]);
 | 
					              let minTimestamp = Math.min(authorship[3], nextAuthorship[3])
 | 
				
			||||||
                            var maxTimestamp = Math.max(authorship[3], nextAuthorship[3]);
 | 
					              let maxTimestamp = Math.max(authorship[3], nextAuthorship[3])
 | 
				
			||||||
                            authorships.splice(j, 1, [authorship[0], authorship[1], nextAuthorship[2], minTimestamp, maxTimestamp]);
 | 
					              authorships.splice(j, 1, [authorship[0], authorship[1], nextAuthorship[2], minTimestamp, maxTimestamp])
 | 
				
			||||||
                            authorships.splice(k, 1);
 | 
					              authorships.splice(k, 1)
 | 
				
			||||||
                            j -= 1;
 | 
					              j -= 1
 | 
				
			||||||
                            break;
 | 
					              break
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        // clear
 | 
					        // clear
 | 
				
			||||||
                for (var j = 0; j < authorships.length; j++) {
 | 
					        for (let j = 0; j < authorships.length; j++) {
 | 
				
			||||||
                    var authorship = authorships[j];
 | 
					          let authorship = authorships[j]
 | 
				
			||||||
          if (!authorship[0]) {
 | 
					          if (!authorship[0]) {
 | 
				
			||||||
                        authorships.splice(j, 1);
 | 
					            authorships.splice(j, 1)
 | 
				
			||||||
                        j -= 1;
 | 
					            j -= 1
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
                return authorships;
 | 
					        return authorships
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      transformPatchToOperations: function (patch, contentLength) {
 | 
					      transformPatchToOperations: function (patch, contentLength) {
 | 
				
			||||||
                var operations = [];
 | 
					        var operations = []
 | 
				
			||||||
        if (patch.length > 0) {
 | 
					        if (patch.length > 0) {
 | 
				
			||||||
          // calculate original content length
 | 
					          // calculate original content length
 | 
				
			||||||
                    for (var j = patch.length - 1; j >= 0; j--) {
 | 
					          for (let j = patch.length - 1; j >= 0; j--) {
 | 
				
			||||||
                        var p = patch[j];
 | 
					            var p = patch[j]
 | 
				
			||||||
                        for (var i = 0; i < p.diffs.length; i++) {
 | 
					            for (let i = 0; i < p.diffs.length; i++) {
 | 
				
			||||||
                            var diff = p.diffs[i];
 | 
					              var diff = p.diffs[i]
 | 
				
			||||||
                            switch(diff[0]) {
 | 
					              switch (diff[0]) {
 | 
				
			||||||
                case 1: // insert
 | 
					                case 1: // insert
 | 
				
			||||||
                                    contentLength -= diff[1].length;
 | 
					                  contentLength -= diff[1].length
 | 
				
			||||||
                                break;
 | 
					                  break
 | 
				
			||||||
                case -1: // delete
 | 
					                case -1: // delete
 | 
				
			||||||
                                    contentLength += diff[1].length;
 | 
					                  contentLength += diff[1].length
 | 
				
			||||||
                                break;
 | 
					                  break
 | 
				
			||||||
              }
 | 
					              }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
          // generate operations
 | 
					          // generate operations
 | 
				
			||||||
                    var bias = 0;
 | 
					          var bias = 0
 | 
				
			||||||
                    var lengthBias = 0;
 | 
					          var lengthBias = 0
 | 
				
			||||||
                    for (var j = 0; j < patch.length; j++) {
 | 
					          for (let j = 0; j < patch.length; j++) {
 | 
				
			||||||
                        var operation = [];
 | 
					            var operation = []
 | 
				
			||||||
                        var p = patch[j];
 | 
					            let p = patch[j]
 | 
				
			||||||
                        var currIndex = p.start1;
 | 
					            var currIndex = p.start1
 | 
				
			||||||
                        var currLength = contentLength - bias;
 | 
					            var currLength = contentLength - bias
 | 
				
			||||||
                        for (var i = 0; i < p.diffs.length; i++) {
 | 
					            for (let i = 0; i < p.diffs.length; i++) {
 | 
				
			||||||
                            var diff = p.diffs[i];
 | 
					              let diff = p.diffs[i]
 | 
				
			||||||
                            switch(diff[0]) {
 | 
					              switch (diff[0]) {
 | 
				
			||||||
                case 0: // retain
 | 
					                case 0: // retain
 | 
				
			||||||
                                    if (i == 0) // first
 | 
					                  if (i === 0) {
 | 
				
			||||||
                                        operation.push(currIndex + diff[1].length);
 | 
					                    // first
 | 
				
			||||||
                                    else if (i != p.diffs.length - 1) // mid
 | 
					                    operation.push(currIndex + diff[1].length)
 | 
				
			||||||
                                        operation.push(diff[1].length);
 | 
					                  } else if (i !== p.diffs.length - 1) {
 | 
				
			||||||
                                    else // last
 | 
					                    // mid
 | 
				
			||||||
                                        operation.push(currLength + lengthBias - currIndex);
 | 
					                    operation.push(diff[1].length)
 | 
				
			||||||
                                    currIndex += diff[1].length;
 | 
					                  } else {
 | 
				
			||||||
                                break;
 | 
					                    // last
 | 
				
			||||||
 | 
					                    operation.push(currLength + lengthBias - currIndex)
 | 
				
			||||||
 | 
					                  }
 | 
				
			||||||
 | 
					                  currIndex += diff[1].length
 | 
				
			||||||
 | 
					                  break
 | 
				
			||||||
                case 1: // insert
 | 
					                case 1: // insert
 | 
				
			||||||
                                    operation.push(diff[1]);
 | 
					                  operation.push(diff[1])
 | 
				
			||||||
                                    lengthBias += diff[1].length;
 | 
					                  lengthBias += diff[1].length
 | 
				
			||||||
                                    currIndex += diff[1].length;
 | 
					                  currIndex += diff[1].length
 | 
				
			||||||
                                break;
 | 
					                  break
 | 
				
			||||||
                case -1: // delete
 | 
					                case -1: // delete
 | 
				
			||||||
                                    operation.push(-diff[1].length);
 | 
					                  operation.push(-diff[1].length)
 | 
				
			||||||
                                    bias += diff[1].length;
 | 
					                  bias += diff[1].length
 | 
				
			||||||
                                    currIndex += diff[1].length;
 | 
					                  currIndex += diff[1].length
 | 
				
			||||||
                                break;
 | 
					                  break
 | 
				
			||||||
              }
 | 
					              }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
                        operations.push(operation);
 | 
					            operations.push(operation)
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
                return operations;
 | 
					        return operations
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    hooks: {
 | 
					    hooks: {
 | 
				
			||||||
      beforeCreate: function (note, options, callback) {
 | 
					      beforeCreate: function (note, options, callback) {
 | 
				
			||||||
        // if no content specified then use default note
 | 
					        // if no content specified then use default note
 | 
				
			||||||
        if (!note.content) {
 | 
					        if (!note.content) {
 | 
				
			||||||
                    var body = null;
 | 
					          var body = null
 | 
				
			||||||
                    var filePath = null;
 | 
					          let filePath = null
 | 
				
			||||||
          if (!note.alias) {
 | 
					          if (!note.alias) {
 | 
				
			||||||
                        filePath = config.defaultnotepath;
 | 
					            filePath = config.defaultnotepath
 | 
				
			||||||
          } else {
 | 
					          } else {
 | 
				
			||||||
                        filePath = path.join(config.docspath, note.alias + '.md');
 | 
					            filePath = path.join(config.docspath, note.alias + '.md')
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
          if (Note.checkFileExist(filePath)) {
 | 
					          if (Note.checkFileExist(filePath)) {
 | 
				
			||||||
                        var fsCreatedTime = moment(fs.statSync(filePath).ctime);
 | 
					            var fsCreatedTime = moment(fs.statSync(filePath).ctime)
 | 
				
			||||||
                        body = fs.readFileSync(filePath, 'utf8');
 | 
					            body = fs.readFileSync(filePath, 'utf8')
 | 
				
			||||||
                        note.title = Note.parseNoteTitle(body);
 | 
					            note.title = Note.parseNoteTitle(body)
 | 
				
			||||||
                        note.content = body;
 | 
					            note.content = body
 | 
				
			||||||
            if (filePath !== config.defaultnotepath) {
 | 
					            if (filePath !== config.defaultnotepath) {
 | 
				
			||||||
                            note.createdAt = fsCreatedTime;
 | 
					              note.createdAt = fsCreatedTime
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        // if no permission specified and have owner then give default permission in config, else default permission is freely
 | 
					        // if no permission specified and have owner then give default permission in config, else default permission is freely
 | 
				
			||||||
        if (!note.permission) {
 | 
					        if (!note.permission) {
 | 
				
			||||||
          if (note.ownerId) {
 | 
					          if (note.ownerId) {
 | 
				
			||||||
                        note.permission = config.defaultpermission;
 | 
					            note.permission = config.defaultpermission
 | 
				
			||||||
          } else {
 | 
					          } else {
 | 
				
			||||||
                        note.permission = "freely";
 | 
					            note.permission = 'freely'
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
                return callback(null, note);
 | 
					        return callback(null, note)
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      afterCreate: function (note, options, callback) {
 | 
					      afterCreate: function (note, options, callback) {
 | 
				
			||||||
        sequelize.models.Revision.saveNoteRevision(note, function (err, revision) {
 | 
					        sequelize.models.Revision.saveNoteRevision(note, function (err, revision) {
 | 
				
			||||||
                    callback(err, note);
 | 
					          callback(err, note)
 | 
				
			||||||
                });
 | 
					        })
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    });
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return Note;
 | 
					  return Note
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,58 +1,56 @@
 | 
				
			|||||||
"use strict";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// external modules
 | 
					// external modules
 | 
				
			||||||
var Sequelize = require("sequelize");
 | 
					var Sequelize = require('sequelize')
 | 
				
			||||||
var async = require('async');
 | 
					var async = require('async')
 | 
				
			||||||
var moment = require('moment');
 | 
					var moment = require('moment')
 | 
				
			||||||
var childProcess = require('child_process');
 | 
					var childProcess = require('child_process')
 | 
				
			||||||
var shortId = require('shortid');
 | 
					var shortId = require('shortid')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// core
 | 
					// core
 | 
				
			||||||
var config = require("../config.js");
 | 
					var config = require('../config.js')
 | 
				
			||||||
var logger = require("../logger.js");
 | 
					var logger = require('../logger.js')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var dmpWorker = createDmpWorker();
 | 
					var dmpWorker = createDmpWorker()
 | 
				
			||||||
var dmpCallbackCache = {};
 | 
					var dmpCallbackCache = {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function createDmpWorker() {
 | 
					function createDmpWorker () {
 | 
				
			||||||
    var worker = childProcess.fork("./lib/workers/dmpWorker.js", {
 | 
					  var worker = childProcess.fork('./lib/workers/dmpWorker.js', {
 | 
				
			||||||
    stdio: 'ignore'
 | 
					    stdio: 'ignore'
 | 
				
			||||||
    });
 | 
					  })
 | 
				
			||||||
    if (config.debug) logger.info('dmp worker process started');
 | 
					  if (config.debug) logger.info('dmp worker process started')
 | 
				
			||||||
  worker.on('message', function (data) {
 | 
					  worker.on('message', function (data) {
 | 
				
			||||||
    if (!data || !data.msg || !data.cacheKey) {
 | 
					    if (!data || !data.msg || !data.cacheKey) {
 | 
				
			||||||
            return logger.error('dmp worker error: not enough data on message');
 | 
					      return logger.error('dmp worker error: not enough data on message')
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
        var cacheKey = data.cacheKey;
 | 
					    var cacheKey = data.cacheKey
 | 
				
			||||||
        switch(data.msg) {
 | 
					    switch (data.msg) {
 | 
				
			||||||
      case 'error':
 | 
					      case 'error':
 | 
				
			||||||
                dmpCallbackCache[cacheKey](data.error, null);
 | 
					        dmpCallbackCache[cacheKey](data.error, null)
 | 
				
			||||||
                break;
 | 
					        break
 | 
				
			||||||
      case 'check':
 | 
					      case 'check':
 | 
				
			||||||
                dmpCallbackCache[cacheKey](null, data.result);
 | 
					        dmpCallbackCache[cacheKey](null, data.result)
 | 
				
			||||||
                break;
 | 
					        break
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
        delete dmpCallbackCache[cacheKey];
 | 
					    delete dmpCallbackCache[cacheKey]
 | 
				
			||||||
    });
 | 
					  })
 | 
				
			||||||
  worker.on('close', function (code) {
 | 
					  worker.on('close', function (code) {
 | 
				
			||||||
        dmpWorker = null;
 | 
					    dmpWorker = null
 | 
				
			||||||
        if (config.debug) logger.info('dmp worker process exited with code ' + code);
 | 
					    if (config.debug) logger.info('dmp worker process exited with code ' + code)
 | 
				
			||||||
    });
 | 
					  })
 | 
				
			||||||
    return worker;
 | 
					  return worker
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function sendDmpWorker(data, callback) {
 | 
					function sendDmpWorker (data, callback) {
 | 
				
			||||||
    if (!dmpWorker) dmpWorker = createDmpWorker();
 | 
					  if (!dmpWorker) dmpWorker = createDmpWorker()
 | 
				
			||||||
    var cacheKey = Date.now() + '_' + shortId.generate();
 | 
					  var cacheKey = Date.now() + '_' + shortId.generate()
 | 
				
			||||||
    dmpCallbackCache[cacheKey] = callback;
 | 
					  dmpCallbackCache[cacheKey] = callback
 | 
				
			||||||
  data = Object.assign(data, {
 | 
					  data = Object.assign(data, {
 | 
				
			||||||
    cacheKey: cacheKey
 | 
					    cacheKey: cacheKey
 | 
				
			||||||
    });
 | 
					  })
 | 
				
			||||||
    dmpWorker.send(data);
 | 
					  dmpWorker.send(data)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
module.exports = function (sequelize, DataTypes) {
 | 
					module.exports = function (sequelize, DataTypes) {
 | 
				
			||||||
    var Revision = sequelize.define("Revision", {
 | 
					  var Revision = sequelize.define('Revision', {
 | 
				
			||||||
    id: {
 | 
					    id: {
 | 
				
			||||||
      type: DataTypes.UUID,
 | 
					      type: DataTypes.UUID,
 | 
				
			||||||
      primaryKey: true,
 | 
					      primaryKey: true,
 | 
				
			||||||
@ -61,28 +59,28 @@ module.exports = function (sequelize, DataTypes) {
 | 
				
			|||||||
    patch: {
 | 
					    patch: {
 | 
				
			||||||
      type: DataTypes.TEXT,
 | 
					      type: DataTypes.TEXT,
 | 
				
			||||||
      get: function () {
 | 
					      get: function () {
 | 
				
			||||||
                return sequelize.processData(this.getDataValue('patch'), "");
 | 
					        return sequelize.processData(this.getDataValue('patch'), '')
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      set: function (value) {
 | 
					      set: function (value) {
 | 
				
			||||||
                this.setDataValue('patch', sequelize.stripNullByte(value));
 | 
					        this.setDataValue('patch', sequelize.stripNullByte(value))
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    lastContent: {
 | 
					    lastContent: {
 | 
				
			||||||
      type: DataTypes.TEXT,
 | 
					      type: DataTypes.TEXT,
 | 
				
			||||||
      get: function () {
 | 
					      get: function () {
 | 
				
			||||||
                return sequelize.processData(this.getDataValue('lastContent'), "");
 | 
					        return sequelize.processData(this.getDataValue('lastContent'), '')
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      set: function (value) {
 | 
					      set: function (value) {
 | 
				
			||||||
                this.setDataValue('lastContent', sequelize.stripNullByte(value));
 | 
					        this.setDataValue('lastContent', sequelize.stripNullByte(value))
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    content: {
 | 
					    content: {
 | 
				
			||||||
      type: DataTypes.TEXT,
 | 
					      type: DataTypes.TEXT,
 | 
				
			||||||
      get: function () {
 | 
					      get: function () {
 | 
				
			||||||
                return sequelize.processData(this.getDataValue('content'), "");
 | 
					        return sequelize.processData(this.getDataValue('content'), '')
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      set: function (value) {
 | 
					      set: function (value) {
 | 
				
			||||||
                this.setDataValue('content', sequelize.stripNullByte(value));
 | 
					        this.setDataValue('content', sequelize.stripNullByte(value))
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    length: {
 | 
					    length: {
 | 
				
			||||||
@ -91,20 +89,20 @@ module.exports = function (sequelize, DataTypes) {
 | 
				
			|||||||
    authorship: {
 | 
					    authorship: {
 | 
				
			||||||
      type: DataTypes.TEXT,
 | 
					      type: DataTypes.TEXT,
 | 
				
			||||||
      get: function () {
 | 
					      get: function () {
 | 
				
			||||||
                return sequelize.processData(this.getDataValue('authorship'), [], JSON.parse);
 | 
					        return sequelize.processData(this.getDataValue('authorship'), [], JSON.parse)
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      set: function (value) {
 | 
					      set: function (value) {
 | 
				
			||||||
                this.setDataValue('authorship', value ? JSON.stringify(value) : value);
 | 
					        this.setDataValue('authorship', value ? JSON.stringify(value) : value)
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }, {
 | 
					  }, {
 | 
				
			||||||
    classMethods: {
 | 
					    classMethods: {
 | 
				
			||||||
      associate: function (models) {
 | 
					      associate: function (models) {
 | 
				
			||||||
        Revision.belongsTo(models.Note, {
 | 
					        Revision.belongsTo(models.Note, {
 | 
				
			||||||
                    foreignKey: "noteId",
 | 
					          foreignKey: 'noteId',
 | 
				
			||||||
                    as: "note",
 | 
					          as: 'note',
 | 
				
			||||||
          constraints: false
 | 
					          constraints: false
 | 
				
			||||||
                });
 | 
					        })
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      getNoteRevisions: function (note, callback) {
 | 
					      getNoteRevisions: function (note, callback) {
 | 
				
			||||||
        Revision.findAll({
 | 
					        Revision.findAll({
 | 
				
			||||||
@ -113,18 +111,18 @@ module.exports = function (sequelize, DataTypes) {
 | 
				
			|||||||
          },
 | 
					          },
 | 
				
			||||||
          order: '"createdAt" DESC'
 | 
					          order: '"createdAt" DESC'
 | 
				
			||||||
        }).then(function (revisions) {
 | 
					        }).then(function (revisions) {
 | 
				
			||||||
                    var data = [];
 | 
					          var data = []
 | 
				
			||||||
          for (var i = 0, l = revisions.length; i < l; i++) {
 | 
					          for (var i = 0, l = revisions.length; i < l; i++) {
 | 
				
			||||||
                        var revision = revisions[i];
 | 
					            var revision = revisions[i]
 | 
				
			||||||
            data.push({
 | 
					            data.push({
 | 
				
			||||||
              time: moment(revision.createdAt).valueOf(),
 | 
					              time: moment(revision.createdAt).valueOf(),
 | 
				
			||||||
              length: revision.length
 | 
					              length: revision.length
 | 
				
			||||||
                        });
 | 
					            })
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
                    callback(null, data);
 | 
					          callback(null, data)
 | 
				
			||||||
        }).catch(function (err) {
 | 
					        }).catch(function (err) {
 | 
				
			||||||
                    callback(err, null);
 | 
					          callback(err, null)
 | 
				
			||||||
                });
 | 
					        })
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      getPatchedNoteRevisionByTime: function (note, time, callback) {
 | 
					      getPatchedNoteRevisionByTime: function (note, time, callback) {
 | 
				
			||||||
        // find all revisions to prepare for all possible calculation
 | 
					        // find all revisions to prepare for all possible calculation
 | 
				
			||||||
@ -134,7 +132,7 @@ module.exports = function (sequelize, DataTypes) {
 | 
				
			|||||||
          },
 | 
					          },
 | 
				
			||||||
          order: '"createdAt" DESC'
 | 
					          order: '"createdAt" DESC'
 | 
				
			||||||
        }).then(function (revisions) {
 | 
					        }).then(function (revisions) {
 | 
				
			||||||
                    if (revisions.length <= 0) return callback(null, null);
 | 
					          if (revisions.length <= 0) return callback(null, null)
 | 
				
			||||||
          // measure target revision position
 | 
					          // measure target revision position
 | 
				
			||||||
          Revision.count({
 | 
					          Revision.count({
 | 
				
			||||||
            where: {
 | 
					            where: {
 | 
				
			||||||
@ -145,28 +143,28 @@ module.exports = function (sequelize, DataTypes) {
 | 
				
			|||||||
            },
 | 
					            },
 | 
				
			||||||
            order: '"createdAt" DESC'
 | 
					            order: '"createdAt" DESC'
 | 
				
			||||||
          }).then(function (count) {
 | 
					          }).then(function (count) {
 | 
				
			||||||
                        if (count <= 0) return callback(null, null);
 | 
					            if (count <= 0) return callback(null, null)
 | 
				
			||||||
            sendDmpWorker({
 | 
					            sendDmpWorker({
 | 
				
			||||||
              msg: 'get revision',
 | 
					              msg: 'get revision',
 | 
				
			||||||
              revisions: revisions,
 | 
					              revisions: revisions,
 | 
				
			||||||
              count: count
 | 
					              count: count
 | 
				
			||||||
                        }, callback);
 | 
					            }, callback)
 | 
				
			||||||
          }).catch(function (err) {
 | 
					          }).catch(function (err) {
 | 
				
			||||||
                        return callback(err, null);
 | 
					            return callback(err, null)
 | 
				
			||||||
                    });
 | 
					          })
 | 
				
			||||||
        }).catch(function (err) {
 | 
					        }).catch(function (err) {
 | 
				
			||||||
                    return callback(err, null);
 | 
					          return callback(err, null)
 | 
				
			||||||
                });
 | 
					        })
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      checkAllNotesRevision: function (callback) {
 | 
					      checkAllNotesRevision: function (callback) {
 | 
				
			||||||
        Revision.saveAllNotesRevision(function (err, notes) {
 | 
					        Revision.saveAllNotesRevision(function (err, notes) {
 | 
				
			||||||
                    if (err) return callback(err, null);
 | 
					          if (err) return callback(err, null)
 | 
				
			||||||
          if (!notes || notes.length <= 0) {
 | 
					          if (!notes || notes.length <= 0) {
 | 
				
			||||||
                        return callback(null, notes);
 | 
					            return callback(null, notes)
 | 
				
			||||||
          } else {
 | 
					          } else {
 | 
				
			||||||
                        Revision.checkAllNotesRevision(callback);
 | 
					            Revision.checkAllNotesRevision(callback)
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
                });
 | 
					        })
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      saveAllNotesRevision: function (callback) {
 | 
					      saveAllNotesRevision: function (callback) {
 | 
				
			||||||
        sequelize.models.Note.findAll({
 | 
					        sequelize.models.Note.findAll({
 | 
				
			||||||
@ -195,35 +193,37 @@ module.exports = function (sequelize, DataTypes) {
 | 
				
			|||||||
            ]
 | 
					            ]
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        }).then(function (notes) {
 | 
					        }).then(function (notes) {
 | 
				
			||||||
                    if (notes.length <= 0) return callback(null, notes);
 | 
					          if (notes.length <= 0) return callback(null, notes)
 | 
				
			||||||
                    var savedNotes = [];
 | 
					          var savedNotes = []
 | 
				
			||||||
          async.each(notes, function (note, _callback) {
 | 
					          async.each(notes, function (note, _callback) {
 | 
				
			||||||
            // revision saving policy: note not been modified for 5 mins or not save for 10 mins
 | 
					            // revision saving policy: note not been modified for 5 mins or not save for 10 mins
 | 
				
			||||||
            if (note.lastchangeAt && note.savedAt) {
 | 
					            if (note.lastchangeAt && note.savedAt) {
 | 
				
			||||||
                            var lastchangeAt = moment(note.lastchangeAt);
 | 
					              var lastchangeAt = moment(note.lastchangeAt)
 | 
				
			||||||
                            var savedAt = moment(note.savedAt);
 | 
					              var savedAt = moment(note.savedAt)
 | 
				
			||||||
              if (moment().isAfter(lastchangeAt.add(5, 'minutes'))) {
 | 
					              if (moment().isAfter(lastchangeAt.add(5, 'minutes'))) {
 | 
				
			||||||
                                savedNotes.push(note);
 | 
					                savedNotes.push(note)
 | 
				
			||||||
                                Revision.saveNoteRevision(note, _callback);
 | 
					                Revision.saveNoteRevision(note, _callback)
 | 
				
			||||||
              } else if (lastchangeAt.isAfter(savedAt.add(10, 'minutes'))) {
 | 
					              } else if (lastchangeAt.isAfter(savedAt.add(10, 'minutes'))) {
 | 
				
			||||||
                                savedNotes.push(note);
 | 
					                savedNotes.push(note)
 | 
				
			||||||
                                Revision.saveNoteRevision(note, _callback);
 | 
					                Revision.saveNoteRevision(note, _callback)
 | 
				
			||||||
              } else {
 | 
					              } else {
 | 
				
			||||||
                                return _callback(null, null);
 | 
					                return _callback(null, null)
 | 
				
			||||||
              }
 | 
					              }
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
                            savedNotes.push(note);
 | 
					              savedNotes.push(note)
 | 
				
			||||||
                            Revision.saveNoteRevision(note, _callback);
 | 
					              Revision.saveNoteRevision(note, _callback)
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
          }, function (err) {
 | 
					          }, function (err) {
 | 
				
			||||||
                        if (err) return callback(err, null);
 | 
					            if (err) {
 | 
				
			||||||
 | 
					              return callback(err, null)
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
            // return null when no notes need saving at this moment but have delayed tasks to be done
 | 
					            // return null when no notes need saving at this moment but have delayed tasks to be done
 | 
				
			||||||
                        var result = ((savedNotes.length == 0) && (notes.length > savedNotes.length)) ? null : savedNotes;
 | 
					            var result = ((savedNotes.length === 0) && (notes.length > savedNotes.length)) ? null : savedNotes
 | 
				
			||||||
                        return callback(null, result);
 | 
					            return callback(null, result)
 | 
				
			||||||
                    });
 | 
					          })
 | 
				
			||||||
        }).catch(function (err) {
 | 
					        }).catch(function (err) {
 | 
				
			||||||
                    return callback(err, null);
 | 
					          return callback(err, null)
 | 
				
			||||||
                });
 | 
					        })
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      saveNoteRevision: function (note, callback) {
 | 
					      saveNoteRevision: function (note, callback) {
 | 
				
			||||||
        Revision.findAll({
 | 
					        Revision.findAll({
 | 
				
			||||||
@ -240,30 +240,30 @@ module.exports = function (sequelize, DataTypes) {
 | 
				
			|||||||
              length: note.content.length,
 | 
					              length: note.content.length,
 | 
				
			||||||
              authorship: note.authorship
 | 
					              authorship: note.authorship
 | 
				
			||||||
            }).then(function (revision) {
 | 
					            }).then(function (revision) {
 | 
				
			||||||
                            Revision.finishSaveNoteRevision(note, revision, callback);
 | 
					              Revision.finishSaveNoteRevision(note, revision, callback)
 | 
				
			||||||
            }).catch(function (err) {
 | 
					            }).catch(function (err) {
 | 
				
			||||||
                            return callback(err, null);
 | 
					              return callback(err, null)
 | 
				
			||||||
                        });
 | 
					            })
 | 
				
			||||||
          } else {
 | 
					          } else {
 | 
				
			||||||
                        var latestRevision = revisions[0];
 | 
					            var latestRevision = revisions[0]
 | 
				
			||||||
                        var lastContent = latestRevision.content || latestRevision.lastContent;
 | 
					            var lastContent = latestRevision.content || latestRevision.lastContent
 | 
				
			||||||
                        var content = note.content;
 | 
					            var content = note.content
 | 
				
			||||||
            sendDmpWorker({
 | 
					            sendDmpWorker({
 | 
				
			||||||
              msg: 'create patch',
 | 
					              msg: 'create patch',
 | 
				
			||||||
              lastDoc: lastContent,
 | 
					              lastDoc: lastContent,
 | 
				
			||||||
                            currDoc: content,
 | 
					              currDoc: content
 | 
				
			||||||
            }, function (err, patch) {
 | 
					            }, function (err, patch) {
 | 
				
			||||||
                            if (err) logger.error('save note revision error', err);
 | 
					              if (err) logger.error('save note revision error', err)
 | 
				
			||||||
              if (!patch) {
 | 
					              if (!patch) {
 | 
				
			||||||
                // if patch is empty (means no difference) then just update the latest revision updated time
 | 
					                // if patch is empty (means no difference) then just update the latest revision updated time
 | 
				
			||||||
                                latestRevision.changed('updatedAt', true); 
 | 
					                latestRevision.changed('updatedAt', true)
 | 
				
			||||||
                latestRevision.update({
 | 
					                latestRevision.update({
 | 
				
			||||||
                  updatedAt: Date.now()
 | 
					                  updatedAt: Date.now()
 | 
				
			||||||
                }).then(function (revision) {
 | 
					                }).then(function (revision) {
 | 
				
			||||||
                                    Revision.finishSaveNoteRevision(note, revision, callback);
 | 
					                  Revision.finishSaveNoteRevision(note, revision, callback)
 | 
				
			||||||
                }).catch(function (err) {
 | 
					                }).catch(function (err) {
 | 
				
			||||||
                                    return callback(err, null);
 | 
					                  return callback(err, null)
 | 
				
			||||||
                                });
 | 
					                })
 | 
				
			||||||
              } else {
 | 
					              } else {
 | 
				
			||||||
                Revision.create({
 | 
					                Revision.create({
 | 
				
			||||||
                  noteId: note.id,
 | 
					                  noteId: note.id,
 | 
				
			||||||
@ -276,31 +276,31 @@ module.exports = function (sequelize, DataTypes) {
 | 
				
			|||||||
                  latestRevision.update({
 | 
					                  latestRevision.update({
 | 
				
			||||||
                    content: null
 | 
					                    content: null
 | 
				
			||||||
                  }).then(function () {
 | 
					                  }).then(function () {
 | 
				
			||||||
                                        Revision.finishSaveNoteRevision(note, revision, callback);
 | 
					                    Revision.finishSaveNoteRevision(note, revision, callback)
 | 
				
			||||||
                  }).catch(function (err) {
 | 
					                  }).catch(function (err) {
 | 
				
			||||||
                                        return callback(err, null);
 | 
					                    return callback(err, null)
 | 
				
			||||||
                                    });
 | 
					                  })
 | 
				
			||||||
                }).catch(function (err) {
 | 
					                }).catch(function (err) {
 | 
				
			||||||
                                    return callback(err, null);
 | 
					                  return callback(err, null)
 | 
				
			||||||
                                });
 | 
					                })
 | 
				
			||||||
              }
 | 
					              }
 | 
				
			||||||
                        });
 | 
					            })
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        }).catch(function (err) {
 | 
					        }).catch(function (err) {
 | 
				
			||||||
                    return callback(err, null);
 | 
					          return callback(err, null)
 | 
				
			||||||
                });
 | 
					        })
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      finishSaveNoteRevision: function (note, revision, callback) {
 | 
					      finishSaveNoteRevision: function (note, revision, callback) {
 | 
				
			||||||
        note.update({
 | 
					        note.update({
 | 
				
			||||||
          savedAt: revision.updatedAt
 | 
					          savedAt: revision.updatedAt
 | 
				
			||||||
        }).then(function () {
 | 
					        }).then(function () {
 | 
				
			||||||
                    return callback(null, revision);
 | 
					          return callback(null, revision)
 | 
				
			||||||
        }).catch(function (err) {
 | 
					        }).catch(function (err) {
 | 
				
			||||||
                    return callback(err, null);
 | 
					          return callback(err, null)
 | 
				
			||||||
                });
 | 
					        })
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    });
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return Revision;
 | 
					  return Revision
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,10 +1,8 @@
 | 
				
			|||||||
"use strict";
 | 
					// external modules
 | 
				
			||||||
 | 
					var shortId = require('shortid')
 | 
				
			||||||
//external modules
 | 
					 | 
				
			||||||
var shortId = require('shortid');
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
module.exports = function (sequelize, DataTypes) {
 | 
					module.exports = function (sequelize, DataTypes) {
 | 
				
			||||||
    var Temp = sequelize.define("Temp", {
 | 
					  var Temp = sequelize.define('Temp', {
 | 
				
			||||||
    id: {
 | 
					    id: {
 | 
				
			||||||
      type: DataTypes.STRING,
 | 
					      type: DataTypes.STRING,
 | 
				
			||||||
      primaryKey: true,
 | 
					      primaryKey: true,
 | 
				
			||||||
@ -13,7 +11,7 @@ module.exports = function (sequelize, DataTypes) {
 | 
				
			|||||||
    data: {
 | 
					    data: {
 | 
				
			||||||
      type: DataTypes.TEXT
 | 
					      type: DataTypes.TEXT
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    });
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return Temp;
 | 
					  return Temp
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,16 +1,14 @@
 | 
				
			|||||||
"use strict";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// external modules
 | 
					// external modules
 | 
				
			||||||
var md5 = require("blueimp-md5");
 | 
					var md5 = require('blueimp-md5')
 | 
				
			||||||
var Sequelize = require("sequelize");
 | 
					var Sequelize = require('sequelize')
 | 
				
			||||||
var scrypt = require('scrypt');
 | 
					var scrypt = require('scrypt')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// core
 | 
					// core
 | 
				
			||||||
var logger = require("../logger.js");
 | 
					var logger = require('../logger.js')
 | 
				
			||||||
var letterAvatars = require('../letter-avatars.js');
 | 
					var letterAvatars = require('../letter-avatars.js')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
module.exports = function (sequelize, DataTypes) {
 | 
					module.exports = function (sequelize, DataTypes) {
 | 
				
			||||||
    var User = sequelize.define("User", {
 | 
					  var User = sequelize.define('User', {
 | 
				
			||||||
    id: {
 | 
					    id: {
 | 
				
			||||||
      type: DataTypes.UUID,
 | 
					      type: DataTypes.UUID,
 | 
				
			||||||
      primaryKey: true,
 | 
					      primaryKey: true,
 | 
				
			||||||
@ -40,41 +38,41 @@ module.exports = function (sequelize, DataTypes) {
 | 
				
			|||||||
    },
 | 
					    },
 | 
				
			||||||
    password: {
 | 
					    password: {
 | 
				
			||||||
      type: Sequelize.TEXT,
 | 
					      type: Sequelize.TEXT,
 | 
				
			||||||
            set: function(value) {
 | 
					      set: function (value) {
 | 
				
			||||||
                var hash = scrypt.kdfSync(value, scrypt.paramsSync(0.1)).toString("hex");
 | 
					        var hash = scrypt.kdfSync(value, scrypt.paramsSync(0.1)).toString('hex')
 | 
				
			||||||
                this.setDataValue('password', hash);
 | 
					        this.setDataValue('password', hash)
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }, {
 | 
					  }, {
 | 
				
			||||||
    instanceMethods: {
 | 
					    instanceMethods: {
 | 
				
			||||||
            verifyPassword: function(attempt) {
 | 
					      verifyPassword: function (attempt) {
 | 
				
			||||||
                if (scrypt.verifyKdfSync(new Buffer(this.password, "hex"), attempt)) {
 | 
					        if (scrypt.verifyKdfSync(new Buffer(this.password, 'hex'), attempt)) {
 | 
				
			||||||
                    return this;
 | 
					          return this
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
                    return false;
 | 
					          return false
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    classMethods: {
 | 
					    classMethods: {
 | 
				
			||||||
      associate: function (models) {
 | 
					      associate: function (models) {
 | 
				
			||||||
        User.hasMany(models.Note, {
 | 
					        User.hasMany(models.Note, {
 | 
				
			||||||
                    foreignKey: "ownerId",
 | 
					          foreignKey: 'ownerId',
 | 
				
			||||||
          constraints: false
 | 
					          constraints: false
 | 
				
			||||||
                });
 | 
					        })
 | 
				
			||||||
        User.hasMany(models.Note, {
 | 
					        User.hasMany(models.Note, {
 | 
				
			||||||
                    foreignKey: "lastchangeuserId",
 | 
					          foreignKey: 'lastchangeuserId',
 | 
				
			||||||
          constraints: false
 | 
					          constraints: false
 | 
				
			||||||
                });
 | 
					        })
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      getProfile: function (user) {
 | 
					      getProfile: function (user) {
 | 
				
			||||||
                return user.profile ? User.parseProfile(user.profile) : (user.email ? User.parseProfileByEmail(user.email) : null);
 | 
					        return user.profile ? User.parseProfile(user.profile) : (user.email ? User.parseProfileByEmail(user.email) : null)
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      parseProfile: function (profile) {
 | 
					      parseProfile: function (profile) {
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
                    var profile = JSON.parse(profile);
 | 
					          profile = JSON.parse(profile)
 | 
				
			||||||
        } catch (err) {
 | 
					        } catch (err) {
 | 
				
			||||||
                    logger.error(err);
 | 
					          logger.error(err)
 | 
				
			||||||
                    profile = null;
 | 
					          profile = null
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        if (profile) {
 | 
					        if (profile) {
 | 
				
			||||||
          profile = {
 | 
					          profile = {
 | 
				
			||||||
@ -83,67 +81,67 @@ module.exports = function (sequelize, DataTypes) {
 | 
				
			|||||||
            biggerphoto: User.parsePhotoByProfile(profile, true)
 | 
					            biggerphoto: User.parsePhotoByProfile(profile, true)
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
                return profile;
 | 
					        return profile
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      parsePhotoByProfile: function (profile, bigger) {
 | 
					      parsePhotoByProfile: function (profile, bigger) {
 | 
				
			||||||
                var photo = null;
 | 
					        var photo = null
 | 
				
			||||||
        switch (profile.provider) {
 | 
					        switch (profile.provider) {
 | 
				
			||||||
                    case "facebook":
 | 
					          case 'facebook':
 | 
				
			||||||
                        photo = 'https://graph.facebook.com/' + profile.id + '/picture';
 | 
					            photo = 'https://graph.facebook.com/' + profile.id + '/picture'
 | 
				
			||||||
                        if (bigger) photo += '?width=400';
 | 
					            if (bigger) photo += '?width=400'
 | 
				
			||||||
                        else photo += '?width=96';
 | 
					            else photo += '?width=96'
 | 
				
			||||||
                        break;
 | 
					            break
 | 
				
			||||||
                    case "twitter":
 | 
					          case 'twitter':
 | 
				
			||||||
                        photo = 'https://twitter.com/' + profile.username + '/profile_image';
 | 
					            photo = 'https://twitter.com/' + profile.username + '/profile_image'
 | 
				
			||||||
                        if (bigger) photo += '?size=original';
 | 
					            if (bigger) photo += '?size=original'
 | 
				
			||||||
                        else photo += '?size=bigger';
 | 
					            else photo += '?size=bigger'
 | 
				
			||||||
                        break;
 | 
					            break
 | 
				
			||||||
                    case "github":
 | 
					          case 'github':
 | 
				
			||||||
                        photo = 'https://avatars.githubusercontent.com/u/' + profile.id;
 | 
					            photo = 'https://avatars.githubusercontent.com/u/' + profile.id
 | 
				
			||||||
                        if (bigger) photo += '?s=400';
 | 
					            if (bigger) photo += '?s=400'
 | 
				
			||||||
                        else photo += '?s=96';
 | 
					            else photo += '?s=96'
 | 
				
			||||||
                        break;
 | 
					            break
 | 
				
			||||||
                    case "gitlab":
 | 
					          case 'gitlab':
 | 
				
			||||||
                        photo = profile.avatarUrl;
 | 
					            photo = profile.avatarUrl
 | 
				
			||||||
                        if (bigger) photo = photo.replace(/(\?s=)\d*$/i, '$1400');
 | 
					            if (bigger) photo = photo.replace(/(\?s=)\d*$/i, '$1400')
 | 
				
			||||||
                        else photo = photo.replace(/(\?s=)\d*$/i, '$196');
 | 
					            else photo = photo.replace(/(\?s=)\d*$/i, '$196')
 | 
				
			||||||
                        break;
 | 
					            break
 | 
				
			||||||
                    case "dropbox":
 | 
					          case 'dropbox':
 | 
				
			||||||
                        //no image api provided, use gravatar
 | 
					            // no image api provided, use gravatar
 | 
				
			||||||
                        photo = 'https://www.gravatar.com/avatar/' + md5(profile.emails[0].value);
 | 
					            photo = 'https://www.gravatar.com/avatar/' + md5(profile.emails[0].value)
 | 
				
			||||||
                        if (bigger) photo += '?s=400';
 | 
					            if (bigger) photo += '?s=400'
 | 
				
			||||||
                        else photo += '?s=96';
 | 
					            else photo += '?s=96'
 | 
				
			||||||
                        break;
 | 
					            break
 | 
				
			||||||
                    case "google":
 | 
					          case 'google':
 | 
				
			||||||
                        photo = profile.photos[0].value;
 | 
					            photo = profile.photos[0].value
 | 
				
			||||||
                        if (bigger) photo = photo.replace(/(\?sz=)\d*$/i, '$1400');
 | 
					            if (bigger) photo = photo.replace(/(\?sz=)\d*$/i, '$1400')
 | 
				
			||||||
                        else photo = photo.replace(/(\?sz=)\d*$/i, '$196');
 | 
					            else photo = photo.replace(/(\?sz=)\d*$/i, '$196')
 | 
				
			||||||
                        break;
 | 
					            break
 | 
				
			||||||
                    case "ldap":
 | 
					          case 'ldap':
 | 
				
			||||||
                        //no image api provided,
 | 
					            // no image api provided,
 | 
				
			||||||
                        //use gravatar if email exists,
 | 
					            // use gravatar if email exists,
 | 
				
			||||||
                        //otherwise generate a letter avatar
 | 
					            // otherwise generate a letter avatar
 | 
				
			||||||
            if (profile.emails[0]) {
 | 
					            if (profile.emails[0]) {
 | 
				
			||||||
                            photo = 'https://www.gravatar.com/avatar/' + md5(profile.emails[0]);
 | 
					              photo = 'https://www.gravatar.com/avatar/' + md5(profile.emails[0])
 | 
				
			||||||
                            if (bigger) photo += '?s=400';
 | 
					              if (bigger) photo += '?s=400'
 | 
				
			||||||
                            else photo += '?s=96';
 | 
					              else photo += '?s=96'
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
                            photo = letterAvatars(profile.username);
 | 
					              photo = letterAvatars(profile.username)
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
                        break;
 | 
					            break
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
                return photo;
 | 
					        return photo
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      parseProfileByEmail: function (email) {
 | 
					      parseProfileByEmail: function (email) {
 | 
				
			||||||
                var photoUrl = 'https://www.gravatar.com/avatar/' + md5(email);
 | 
					        var photoUrl = 'https://www.gravatar.com/avatar/' + md5(email)
 | 
				
			||||||
        return {
 | 
					        return {
 | 
				
			||||||
                    name: email.substring(0, email.lastIndexOf("@")),
 | 
					          name: email.substring(0, email.lastIndexOf('@')),
 | 
				
			||||||
                    photo: photoUrl += '?s=96',
 | 
					          photo: photoUrl + '?s=96',
 | 
				
			||||||
                    biggerphoto: photoUrl += '?s=400'
 | 
					          biggerphoto: photoUrl + '?s=400'
 | 
				
			||||||
                };
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    });
 | 
					    }
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return User;
 | 
					  return User
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										981
									
								
								lib/realtime.js
									
									
									
									
									
								
							
							
						
						
									
										981
									
								
								lib/realtime.js
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										586
									
								
								lib/response.js
									
									
									
									
									
								
							
							
						
						
									
										586
									
								
								lib/response.js
									
									
									
									
									
								
							@ -1,36 +1,34 @@
 | 
				
			|||||||
//response
 | 
					// response
 | 
				
			||||||
//external modules
 | 
					// external modules
 | 
				
			||||||
var fs = require('fs');
 | 
					var fs = require('fs')
 | 
				
			||||||
var path = require('path');
 | 
					var markdownpdf = require('markdown-pdf')
 | 
				
			||||||
var markdownpdf = require("markdown-pdf");
 | 
					var LZString = require('lz-string')
 | 
				
			||||||
var LZString = require('lz-string');
 | 
					var shortId = require('shortid')
 | 
				
			||||||
var S = require('string');
 | 
					var querystring = require('querystring')
 | 
				
			||||||
var shortId = require('shortid');
 | 
					var request = require('request')
 | 
				
			||||||
var querystring = require('querystring');
 | 
					var moment = require('moment')
 | 
				
			||||||
var request = require('request');
 | 
					 | 
				
			||||||
var moment = require('moment');
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
//core
 | 
					// core
 | 
				
			||||||
var config = require("./config.js");
 | 
					var config = require('./config.js')
 | 
				
			||||||
var logger = require("./logger.js");
 | 
					var logger = require('./logger.js')
 | 
				
			||||||
var models = require("./models");
 | 
					var models = require('./models')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//public
 | 
					// public
 | 
				
			||||||
var response = {
 | 
					var response = {
 | 
				
			||||||
  errorForbidden: function (res) {
 | 
					  errorForbidden: function (res) {
 | 
				
			||||||
        responseError(res, "403", "Forbidden", "oh no.");
 | 
					    responseError(res, '403', 'Forbidden', 'oh no.')
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  errorNotFound: function (res) {
 | 
					  errorNotFound: function (res) {
 | 
				
			||||||
        responseError(res, "404", "Not Found", "oops.");
 | 
					    responseError(res, '404', 'Not Found', 'oops.')
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  errorBadRequest: function (res) {
 | 
					  errorBadRequest: function (res) {
 | 
				
			||||||
        responseError(res, "400", "Bad Request", "something not right.");
 | 
					    responseError(res, '400', 'Bad Request', 'something not right.')
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  errorInternalError: function (res) {
 | 
					  errorInternalError: function (res) {
 | 
				
			||||||
        responseError(res, "500", "Internal Error", "wtf.");
 | 
					    responseError(res, '500', 'Internal Error', 'wtf.')
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  errorServiceUnavailable: function (res) {
 | 
					  errorServiceUnavailable: function (res) {
 | 
				
			||||||
        res.status(503).send("I'm busy right now, try again later.");
 | 
					    res.status(503).send("I'm busy right now, try again later.")
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  newNote: newNote,
 | 
					  newNote: newNote,
 | 
				
			||||||
  showNote: showNote,
 | 
					  showNote: showNote,
 | 
				
			||||||
@ -42,9 +40,9 @@ var response = {
 | 
				
			|||||||
  publishSlideActions: publishSlideActions,
 | 
					  publishSlideActions: publishSlideActions,
 | 
				
			||||||
  githubActions: githubActions,
 | 
					  githubActions: githubActions,
 | 
				
			||||||
  gitlabActions: gitlabActions
 | 
					  gitlabActions: gitlabActions
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function responseError(res, code, detail, msg) {
 | 
					function responseError (res, code, detail, msg) {
 | 
				
			||||||
  res.status(code).render(config.errorpath, {
 | 
					  res.status(code).render(config.errorpath, {
 | 
				
			||||||
    url: config.serverurl,
 | 
					    url: config.serverurl,
 | 
				
			||||||
    title: code + ' ' + detail + ' ' + msg,
 | 
					    title: code + ' ' + detail + ' ' + msg,
 | 
				
			||||||
@ -52,10 +50,10 @@ function responseError(res, code, detail, msg) {
 | 
				
			|||||||
    detail: detail,
 | 
					    detail: detail,
 | 
				
			||||||
    msg: msg,
 | 
					    msg: msg,
 | 
				
			||||||
    useCDN: config.usecdn
 | 
					    useCDN: config.usecdn
 | 
				
			||||||
    });
 | 
					  })
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function showIndex(req, res, next) {
 | 
					function showIndex (req, res, next) {
 | 
				
			||||||
  res.render(config.indexpath, {
 | 
					  res.render(config.indexpath, {
 | 
				
			||||||
    url: config.serverurl,
 | 
					    url: config.serverurl,
 | 
				
			||||||
    useCDN: config.usecdn,
 | 
					    useCDN: config.usecdn,
 | 
				
			||||||
@ -72,19 +70,19 @@ function showIndex(req, res, next) {
 | 
				
			|||||||
    signin: req.isAuthenticated(),
 | 
					    signin: req.isAuthenticated(),
 | 
				
			||||||
    infoMessage: req.flash('info'),
 | 
					    infoMessage: req.flash('info'),
 | 
				
			||||||
    errorMessage: req.flash('error')
 | 
					    errorMessage: req.flash('error')
 | 
				
			||||||
    });
 | 
					  })
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function responseHackMD(res, note) {
 | 
					function responseHackMD (res, note) {
 | 
				
			||||||
    var body = note.content;
 | 
					  var body = note.content
 | 
				
			||||||
    var extracted = models.Note.extractMeta(body);
 | 
					  var extracted = models.Note.extractMeta(body)
 | 
				
			||||||
    var meta = models.Note.parseMeta(extracted.meta);
 | 
					  var meta = models.Note.parseMeta(extracted.meta)
 | 
				
			||||||
    var title = models.Note.decodeTitle(note.title);
 | 
					  var title = models.Note.decodeTitle(note.title)
 | 
				
			||||||
    title = models.Note.generateWebTitle(meta.title || title);
 | 
					  title = models.Note.generateWebTitle(meta.title || title)
 | 
				
			||||||
  res.set({
 | 
					  res.set({
 | 
				
			||||||
    'Cache-Control': 'private', // only cache by client
 | 
					    'Cache-Control': 'private', // only cache by client
 | 
				
			||||||
    'X-Robots-Tag': 'noindex, nofollow' // prevent crawling
 | 
					    'X-Robots-Tag': 'noindex, nofollow' // prevent crawling
 | 
				
			||||||
    });
 | 
					  })
 | 
				
			||||||
  res.render(config.hackmdpath, {
 | 
					  res.render(config.hackmdpath, {
 | 
				
			||||||
    url: config.serverurl,
 | 
					    url: config.serverurl,
 | 
				
			||||||
    title: title,
 | 
					    title: title,
 | 
				
			||||||
@ -99,47 +97,44 @@ function responseHackMD(res, note) {
 | 
				
			|||||||
    ldap: config.ldap,
 | 
					    ldap: config.ldap,
 | 
				
			||||||
    email: config.email,
 | 
					    email: config.email,
 | 
				
			||||||
    allowemailregister: config.allowemailregister
 | 
					    allowemailregister: config.allowemailregister
 | 
				
			||||||
    });
 | 
					  })
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function newNote(req, res, next) {
 | 
					function newNote (req, res, next) {
 | 
				
			||||||
    var owner = null;
 | 
					  var owner = null
 | 
				
			||||||
  if (req.isAuthenticated()) {
 | 
					  if (req.isAuthenticated()) {
 | 
				
			||||||
        owner = req.user.id;
 | 
					    owner = req.user.id
 | 
				
			||||||
  } else if (!config.allowanonymous) {
 | 
					  } else if (!config.allowanonymous) {
 | 
				
			||||||
        return response.errorForbidden(res);
 | 
					    return response.errorForbidden(res)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  models.Note.create({
 | 
					  models.Note.create({
 | 
				
			||||||
    ownerId: owner,
 | 
					    ownerId: owner,
 | 
				
			||||||
    alias: req.alias ? req.alias : null
 | 
					    alias: req.alias ? req.alias : null
 | 
				
			||||||
  }).then(function (note) {
 | 
					  }).then(function (note) {
 | 
				
			||||||
        return res.redirect(config.serverurl + "/" + LZString.compressToBase64(note.id));
 | 
					    return res.redirect(config.serverurl + '/' + LZString.compressToBase64(note.id))
 | 
				
			||||||
  }).catch(function (err) {
 | 
					  }).catch(function (err) {
 | 
				
			||||||
        logger.error(err);
 | 
					    logger.error(err)
 | 
				
			||||||
        return response.errorInternalError(res);
 | 
					    return response.errorInternalError(res)
 | 
				
			||||||
    });
 | 
					  })
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function checkViewPermission(req, note) {
 | 
					function checkViewPermission (req, note) {
 | 
				
			||||||
    if (note.permission == 'private') {
 | 
					  if (note.permission === 'private') {
 | 
				
			||||||
        if (!req.isAuthenticated() || note.ownerId != req.user.id)
 | 
					    if (!req.isAuthenticated() || note.ownerId !== req.user.id) { return false } else { return true }
 | 
				
			||||||
            return false;
 | 
					  } else if (note.permission === 'limited' || note.permission === 'protected') {
 | 
				
			||||||
        else
 | 
					    if (!req.isAuthenticated()) { return false } else { return true }
 | 
				
			||||||
            return true;
 | 
					 | 
				
			||||||
    } else if (note.permission == 'limited' || note.permission == 'protected') {
 | 
					 | 
				
			||||||
        if(!req.isAuthenticated())
 | 
					 | 
				
			||||||
            return false;
 | 
					 | 
				
			||||||
        else
 | 
					 | 
				
			||||||
            return true;
 | 
					 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
        return true;
 | 
					    return true
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function findNote(req, res, callback, include) {
 | 
					function findNote (req, res, callback, include) {
 | 
				
			||||||
    var noteId = req.params.noteId;
 | 
					  var noteId = req.params.noteId
 | 
				
			||||||
    var id = req.params.noteId || req.params.shortid;
 | 
					  var id = req.params.noteId || req.params.shortid
 | 
				
			||||||
  models.Note.parseNoteId(id, function (err, _id) {
 | 
					  models.Note.parseNoteId(id, function (err, _id) {
 | 
				
			||||||
 | 
					    if (err) {
 | 
				
			||||||
 | 
					      logger.log(err)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    models.Note.findOne({
 | 
					    models.Note.findOne({
 | 
				
			||||||
      where: {
 | 
					      where: {
 | 
				
			||||||
        id: _id
 | 
					        id: _id
 | 
				
			||||||
@ -148,61 +143,61 @@ function findNote(req, res, callback, include) {
 | 
				
			|||||||
    }).then(function (note) {
 | 
					    }).then(function (note) {
 | 
				
			||||||
      if (!note) {
 | 
					      if (!note) {
 | 
				
			||||||
        if (config.allowfreeurl && noteId) {
 | 
					        if (config.allowfreeurl && noteId) {
 | 
				
			||||||
                    req.alias = noteId;
 | 
					          req.alias = noteId
 | 
				
			||||||
                    return newNote(req, res);
 | 
					          return newNote(req, res)
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
                    return response.errorNotFound(res);
 | 
					          return response.errorNotFound(res)
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      if (!checkViewPermission(req, note)) {
 | 
					      if (!checkViewPermission(req, note)) {
 | 
				
			||||||
                return response.errorForbidden(res);
 | 
					        return response.errorForbidden(res)
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
                return callback(note);
 | 
					        return callback(note)
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }).catch(function (err) {
 | 
					    }).catch(function (err) {
 | 
				
			||||||
            logger.error(err);
 | 
					      logger.error(err)
 | 
				
			||||||
            return response.errorInternalError(res);
 | 
					      return response.errorInternalError(res)
 | 
				
			||||||
        });
 | 
					    })
 | 
				
			||||||
    });
 | 
					  })
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function showNote(req, res, next) {
 | 
					function showNote (req, res, next) {
 | 
				
			||||||
  findNote(req, res, function (note) {
 | 
					  findNote(req, res, function (note) {
 | 
				
			||||||
    // force to use note id
 | 
					    // force to use note id
 | 
				
			||||||
        var noteId = req.params.noteId;
 | 
					    var noteId = req.params.noteId
 | 
				
			||||||
        var id = LZString.compressToBase64(note.id);
 | 
					    var id = LZString.compressToBase64(note.id)
 | 
				
			||||||
        if ((note.alias && noteId != note.alias) || (!note.alias && noteId != id))
 | 
					    if ((note.alias && noteId !== note.alias) || (!note.alias && noteId !== id)) { return res.redirect(config.serverurl + '/' + (note.alias || id)) }
 | 
				
			||||||
            return res.redirect(config.serverurl + "/" + (note.alias || id));
 | 
					    return responseHackMD(res, note)
 | 
				
			||||||
        return responseHackMD(res, note);
 | 
					  })
 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function showPublishNote(req, res, next) {
 | 
					function showPublishNote (req, res, next) {
 | 
				
			||||||
  var include = [{
 | 
					  var include = [{
 | 
				
			||||||
    model: models.User,
 | 
					    model: models.User,
 | 
				
			||||||
        as: "owner"
 | 
					    as: 'owner'
 | 
				
			||||||
  }, {
 | 
					  }, {
 | 
				
			||||||
    model: models.User,
 | 
					    model: models.User,
 | 
				
			||||||
        as: "lastchangeuser"
 | 
					    as: 'lastchangeuser'
 | 
				
			||||||
    }];
 | 
					  }]
 | 
				
			||||||
  findNote(req, res, function (note) {
 | 
					  findNote(req, res, function (note) {
 | 
				
			||||||
    // force to use short id
 | 
					    // force to use short id
 | 
				
			||||||
        var shortid = req.params.shortid;
 | 
					    var shortid = req.params.shortid
 | 
				
			||||||
        if ((note.alias && shortid != note.alias) || (!note.alias && shortid != note.shortid))
 | 
					    if ((note.alias && shortid !== note.alias) || (!note.alias && shortid !== note.shortid)) {
 | 
				
			||||||
            return res.redirect(config.serverurl + "/s/" + (note.alias || note.shortid));
 | 
					      return res.redirect(config.serverurl + '/s/' + (note.alias || note.shortid))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    note.increment('viewcount').then(function (note) {
 | 
					    note.increment('viewcount').then(function (note) {
 | 
				
			||||||
      if (!note) {
 | 
					      if (!note) {
 | 
				
			||||||
                return response.errorNotFound(res);
 | 
					        return response.errorNotFound(res)
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
            var body = note.content;
 | 
					      var body = note.content
 | 
				
			||||||
            var extracted = models.Note.extractMeta(body);
 | 
					      var extracted = models.Note.extractMeta(body)
 | 
				
			||||||
            markdown = extracted.markdown;
 | 
					      var markdown = extracted.markdown
 | 
				
			||||||
            meta = models.Note.parseMeta(extracted.meta);
 | 
					      var meta = models.Note.parseMeta(extracted.meta)
 | 
				
			||||||
            var createtime = note.createdAt;
 | 
					      var createtime = note.createdAt
 | 
				
			||||||
            var updatetime = note.lastchangeAt;
 | 
					      var updatetime = note.lastchangeAt
 | 
				
			||||||
            var title = models.Note.decodeTitle(note.title);
 | 
					      var title = models.Note.decodeTitle(note.title)
 | 
				
			||||||
            title = models.Note.generateWebTitle(meta.title || title);
 | 
					      title = models.Note.generateWebTitle(meta.title || title)
 | 
				
			||||||
            var origin = config.serverurl;
 | 
					      var origin = config.serverurl
 | 
				
			||||||
      var data = {
 | 
					      var data = {
 | 
				
			||||||
        title: title,
 | 
					        title: title,
 | 
				
			||||||
        description: meta.description || (markdown ? models.Note.generateDescription(markdown) : null),
 | 
					        description: meta.description || (markdown ? models.Note.generateDescription(markdown) : null),
 | 
				
			||||||
@ -216,238 +211,237 @@ function showPublishNote(req, res, next) {
 | 
				
			|||||||
        ownerprofile: note.owner ? models.User.getProfile(note.owner) : null,
 | 
					        ownerprofile: note.owner ? models.User.getProfile(note.owner) : null,
 | 
				
			||||||
        lastchangeuser: note.lastchangeuser ? note.lastchangeuser.id : null,
 | 
					        lastchangeuser: note.lastchangeuser ? note.lastchangeuser.id : null,
 | 
				
			||||||
        lastchangeuserprofile: note.lastchangeuser ? models.User.getProfile(note.lastchangeuser) : null,
 | 
					        lastchangeuserprofile: note.lastchangeuser ? models.User.getProfile(note.lastchangeuser) : null,
 | 
				
			||||||
                robots: meta.robots || false, //default allow robots
 | 
					        robots: meta.robots || false, // default allow robots
 | 
				
			||||||
        GA: meta.GA,
 | 
					        GA: meta.GA,
 | 
				
			||||||
        disqus: meta.disqus
 | 
					        disqus: meta.disqus
 | 
				
			||||||
            };
 | 
					      }
 | 
				
			||||||
            return renderPublish(data, res);
 | 
					      return renderPublish(data, res)
 | 
				
			||||||
    }).catch(function (err) {
 | 
					    }).catch(function (err) {
 | 
				
			||||||
            logger.error(err);
 | 
					      logger.error(err)
 | 
				
			||||||
            return response.errorInternalError(res);
 | 
					      return response.errorInternalError(res)
 | 
				
			||||||
        });
 | 
					    })
 | 
				
			||||||
    }, include);
 | 
					  }, include)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function renderPublish(data, res) {
 | 
					function renderPublish (data, res) {
 | 
				
			||||||
  res.set({
 | 
					  res.set({
 | 
				
			||||||
    'Cache-Control': 'private' // only cache by client
 | 
					    'Cache-Control': 'private' // only cache by client
 | 
				
			||||||
    });
 | 
					  })
 | 
				
			||||||
    res.render(config.prettypath, data);
 | 
					  res.render(config.prettypath, data)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function actionPublish(req, res, note) {
 | 
					function actionPublish (req, res, note) {
 | 
				
			||||||
    res.redirect(config.serverurl + "/s/" + (note.alias || note.shortid));
 | 
					  res.redirect(config.serverurl + '/s/' + (note.alias || note.shortid))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function actionSlide(req, res, note) {
 | 
					function actionSlide (req, res, note) {
 | 
				
			||||||
    res.redirect(config.serverurl + "/p/" + (note.alias || note.shortid));
 | 
					  res.redirect(config.serverurl + '/p/' + (note.alias || note.shortid))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function actionDownload(req, res, note) {
 | 
					function actionDownload (req, res, note) {
 | 
				
			||||||
    var body = note.content;
 | 
					  var body = note.content
 | 
				
			||||||
    var title = models.Note.decodeTitle(note.title);
 | 
					  var title = models.Note.decodeTitle(note.title)
 | 
				
			||||||
    var filename = title;
 | 
					  var filename = title
 | 
				
			||||||
    filename = encodeURIComponent(filename);
 | 
					  filename = encodeURIComponent(filename)
 | 
				
			||||||
  res.set({
 | 
					  res.set({
 | 
				
			||||||
        'Access-Control-Allow-Origin': '*', //allow CORS as API
 | 
					    'Access-Control-Allow-Origin': '*', // allow CORS as API
 | 
				
			||||||
    'Access-Control-Allow-Headers': 'Range',
 | 
					    'Access-Control-Allow-Headers': 'Range',
 | 
				
			||||||
    'Access-Control-Expose-Headers': 'Cache-Control, Content-Encoding, Content-Range',
 | 
					    'Access-Control-Expose-Headers': 'Cache-Control, Content-Encoding, Content-Range',
 | 
				
			||||||
    'Content-Type': 'text/markdown; charset=UTF-8',
 | 
					    'Content-Type': 'text/markdown; charset=UTF-8',
 | 
				
			||||||
    'Cache-Control': 'private',
 | 
					    'Cache-Control': 'private',
 | 
				
			||||||
    'Content-disposition': 'attachment; filename=' + filename + '.md',
 | 
					    'Content-disposition': 'attachment; filename=' + filename + '.md',
 | 
				
			||||||
    'X-Robots-Tag': 'noindex, nofollow' // prevent crawling
 | 
					    'X-Robots-Tag': 'noindex, nofollow' // prevent crawling
 | 
				
			||||||
    });
 | 
					  })
 | 
				
			||||||
    res.send(body);
 | 
					  res.send(body)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function actionInfo(req, res, note) {
 | 
					function actionInfo (req, res, note) {
 | 
				
			||||||
    var body = note.content;
 | 
					  var body = note.content
 | 
				
			||||||
    var extracted = models.Note.extractMeta(body);
 | 
					  var extracted = models.Note.extractMeta(body)
 | 
				
			||||||
    var markdown = extracted.markdown;
 | 
					  var markdown = extracted.markdown
 | 
				
			||||||
    var meta = models.Note.parseMeta(extracted.meta);
 | 
					  var meta = models.Note.parseMeta(extracted.meta)
 | 
				
			||||||
    var createtime = note.createdAt;
 | 
					  var createtime = note.createdAt
 | 
				
			||||||
    var updatetime = note.lastchangeAt;
 | 
					  var updatetime = note.lastchangeAt
 | 
				
			||||||
    var title = models.Note.decodeTitle(note.title);
 | 
					  var title = models.Note.decodeTitle(note.title)
 | 
				
			||||||
  var data = {
 | 
					  var data = {
 | 
				
			||||||
    title: meta.title || title,
 | 
					    title: meta.title || title,
 | 
				
			||||||
    description: meta.description || (markdown ? models.Note.generateDescription(markdown) : null),
 | 
					    description: meta.description || (markdown ? models.Note.generateDescription(markdown) : null),
 | 
				
			||||||
    viewcount: note.viewcount,
 | 
					    viewcount: note.viewcount,
 | 
				
			||||||
    createtime: createtime,
 | 
					    createtime: createtime,
 | 
				
			||||||
    updatetime: updatetime
 | 
					    updatetime: updatetime
 | 
				
			||||||
    };
 | 
					  }
 | 
				
			||||||
  res.set({
 | 
					  res.set({
 | 
				
			||||||
        'Access-Control-Allow-Origin': '*', //allow CORS as API
 | 
					    'Access-Control-Allow-Origin': '*', // allow CORS as API
 | 
				
			||||||
    'Access-Control-Allow-Headers': 'Range',
 | 
					    'Access-Control-Allow-Headers': 'Range',
 | 
				
			||||||
    'Access-Control-Expose-Headers': 'Cache-Control, Content-Encoding, Content-Range',
 | 
					    'Access-Control-Expose-Headers': 'Cache-Control, Content-Encoding, Content-Range',
 | 
				
			||||||
    'Cache-Control': 'private', // only cache by client
 | 
					    'Cache-Control': 'private', // only cache by client
 | 
				
			||||||
    'X-Robots-Tag': 'noindex, nofollow' // prevent crawling
 | 
					    'X-Robots-Tag': 'noindex, nofollow' // prevent crawling
 | 
				
			||||||
    });
 | 
					  })
 | 
				
			||||||
    res.send(data);
 | 
					  res.send(data)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function actionPDF(req, res, note) {
 | 
					function actionPDF (req, res, note) {
 | 
				
			||||||
    var body = note.content;
 | 
					  var body = note.content
 | 
				
			||||||
    var extracted = models.Note.extractMeta(body);
 | 
					  var extracted = models.Note.extractMeta(body)
 | 
				
			||||||
    var title = models.Note.decodeTitle(note.title);
 | 
					  var title = models.Note.decodeTitle(note.title)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (!fs.existsSync(config.tmppath)) {
 | 
					  if (!fs.existsSync(config.tmppath)) {
 | 
				
			||||||
        fs.mkdirSync(config.tmppath);
 | 
					    fs.mkdirSync(config.tmppath)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
    var path = config.tmppath + '/' + Date.now() + '.pdf';
 | 
					  var path = config.tmppath + '/' + Date.now() + '.pdf'
 | 
				
			||||||
  markdownpdf().from.string(extracted.markdown).to(path, function () {
 | 
					  markdownpdf().from.string(extracted.markdown).to(path, function () {
 | 
				
			||||||
        var stream = fs.createReadStream(path);
 | 
					    var stream = fs.createReadStream(path)
 | 
				
			||||||
        var filename = title;
 | 
					    var filename = title
 | 
				
			||||||
    // Be careful of special characters
 | 
					    // Be careful of special characters
 | 
				
			||||||
        filename = encodeURIComponent(filename);
 | 
					    filename = encodeURIComponent(filename)
 | 
				
			||||||
    // Ideally this should strip them
 | 
					    // Ideally this should strip them
 | 
				
			||||||
        res.setHeader('Content-disposition', 'attachment; filename="' + filename + '.pdf"');
 | 
					    res.setHeader('Content-disposition', 'attachment; filename="' + filename + '.pdf"')
 | 
				
			||||||
        res.setHeader('Cache-Control', 'private');
 | 
					    res.setHeader('Cache-Control', 'private')
 | 
				
			||||||
        res.setHeader('Content-Type', 'application/pdf; charset=UTF-8');
 | 
					    res.setHeader('Content-Type', 'application/pdf; charset=UTF-8')
 | 
				
			||||||
        res.setHeader('X-Robots-Tag', 'noindex, nofollow'); // prevent crawling
 | 
					    res.setHeader('X-Robots-Tag', 'noindex, nofollow') // prevent crawling
 | 
				
			||||||
        stream.pipe(res);
 | 
					    stream.pipe(res)
 | 
				
			||||||
        fs.unlink(path);
 | 
					    fs.unlink(path)
 | 
				
			||||||
    });
 | 
					  })
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function actionGist(req, res, note) {
 | 
					function actionGist (req, res, note) {
 | 
				
			||||||
  var data = {
 | 
					  var data = {
 | 
				
			||||||
    client_id: config.github.clientID,
 | 
					    client_id: config.github.clientID,
 | 
				
			||||||
    redirect_uri: config.serverurl + '/auth/github/callback/' + LZString.compressToBase64(note.id) + '/gist',
 | 
					    redirect_uri: config.serverurl + '/auth/github/callback/' + LZString.compressToBase64(note.id) + '/gist',
 | 
				
			||||||
        scope: "gist",
 | 
					    scope: 'gist',
 | 
				
			||||||
    state: shortId.generate()
 | 
					    state: shortId.generate()
 | 
				
			||||||
    };
 | 
					  }
 | 
				
			||||||
    var query = querystring.stringify(data);
 | 
					  var query = querystring.stringify(data)
 | 
				
			||||||
    res.redirect("https://github.com/login/oauth/authorize?" + query);
 | 
					  res.redirect('https://github.com/login/oauth/authorize?' + query)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function actionRevision(req, res, note) {
 | 
					function actionRevision (req, res, note) {
 | 
				
			||||||
    var actionId = req.params.actionId;
 | 
					  var actionId = req.params.actionId
 | 
				
			||||||
  if (actionId) {
 | 
					  if (actionId) {
 | 
				
			||||||
        var time = moment(parseInt(actionId));
 | 
					    var time = moment(parseInt(actionId))
 | 
				
			||||||
    if (time.isValid()) {
 | 
					    if (time.isValid()) {
 | 
				
			||||||
      models.Revision.getPatchedNoteRevisionByTime(note, time, function (err, content) {
 | 
					      models.Revision.getPatchedNoteRevisionByTime(note, time, function (err, content) {
 | 
				
			||||||
        if (err) {
 | 
					        if (err) {
 | 
				
			||||||
                    logger.error(err);
 | 
					          logger.error(err)
 | 
				
			||||||
                    return response.errorInternalError(res);
 | 
					          return response.errorInternalError(res)
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        if (!content) {
 | 
					        if (!content) {
 | 
				
			||||||
                    return response.errorNotFound(res);
 | 
					          return response.errorNotFound(res)
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        res.set({
 | 
					        res.set({
 | 
				
			||||||
                    'Access-Control-Allow-Origin': '*', //allow CORS as API
 | 
					          'Access-Control-Allow-Origin': '*', // allow CORS as API
 | 
				
			||||||
          'Access-Control-Allow-Headers': 'Range',
 | 
					          'Access-Control-Allow-Headers': 'Range',
 | 
				
			||||||
          'Access-Control-Expose-Headers': 'Cache-Control, Content-Encoding, Content-Range',
 | 
					          'Access-Control-Expose-Headers': 'Cache-Control, Content-Encoding, Content-Range',
 | 
				
			||||||
          'Cache-Control': 'private', // only cache by client
 | 
					          'Cache-Control': 'private', // only cache by client
 | 
				
			||||||
          'X-Robots-Tag': 'noindex, nofollow' // prevent crawling
 | 
					          'X-Robots-Tag': 'noindex, nofollow' // prevent crawling
 | 
				
			||||||
                });
 | 
					        })
 | 
				
			||||||
                res.send(content);
 | 
					        res.send(content)
 | 
				
			||||||
            });
 | 
					      })
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
            return response.errorNotFound(res);
 | 
					      return response.errorNotFound(res)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
    models.Revision.getNoteRevisions(note, function (err, data) {
 | 
					    models.Revision.getNoteRevisions(note, function (err, data) {
 | 
				
			||||||
      if (err) {
 | 
					      if (err) {
 | 
				
			||||||
                logger.error(err);
 | 
					        logger.error(err)
 | 
				
			||||||
                return response.errorInternalError(res);
 | 
					        return response.errorInternalError(res)
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      var out = {
 | 
					      var out = {
 | 
				
			||||||
        revision: data
 | 
					        revision: data
 | 
				
			||||||
            };
 | 
					      }
 | 
				
			||||||
      res.set({
 | 
					      res.set({
 | 
				
			||||||
                'Access-Control-Allow-Origin': '*', //allow CORS as API
 | 
					        'Access-Control-Allow-Origin': '*', // allow CORS as API
 | 
				
			||||||
        'Access-Control-Allow-Headers': 'Range',
 | 
					        'Access-Control-Allow-Headers': 'Range',
 | 
				
			||||||
        'Access-Control-Expose-Headers': 'Cache-Control, Content-Encoding, Content-Range',
 | 
					        'Access-Control-Expose-Headers': 'Cache-Control, Content-Encoding, Content-Range',
 | 
				
			||||||
        'Cache-Control': 'private', // only cache by client
 | 
					        'Cache-Control': 'private', // only cache by client
 | 
				
			||||||
        'X-Robots-Tag': 'noindex, nofollow' // prevent crawling
 | 
					        'X-Robots-Tag': 'noindex, nofollow' // prevent crawling
 | 
				
			||||||
            });
 | 
					      })
 | 
				
			||||||
            res.send(out);
 | 
					      res.send(out)
 | 
				
			||||||
        });
 | 
					    })
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function noteActions(req, res, next) {
 | 
					function noteActions (req, res, next) {
 | 
				
			||||||
    var noteId = req.params.noteId;
 | 
					  var noteId = req.params.noteId
 | 
				
			||||||
  findNote(req, res, function (note) {
 | 
					  findNote(req, res, function (note) {
 | 
				
			||||||
        var action = req.params.action;
 | 
					    var action = req.params.action
 | 
				
			||||||
    switch (action) {
 | 
					    switch (action) {
 | 
				
			||||||
        case "publish":
 | 
					      case 'publish':
 | 
				
			||||||
        case "pretty": //pretty deprecated
 | 
					      case 'pretty': // pretty deprecated
 | 
				
			||||||
            actionPublish(req, res, note);
 | 
					        actionPublish(req, res, note)
 | 
				
			||||||
            break;
 | 
					        break
 | 
				
			||||||
        case "slide":
 | 
					      case 'slide':
 | 
				
			||||||
            actionSlide(req, res, note);
 | 
					        actionSlide(req, res, note)
 | 
				
			||||||
            break;
 | 
					        break
 | 
				
			||||||
        case "download":
 | 
					      case 'download':
 | 
				
			||||||
            actionDownload(req, res, note);
 | 
					        actionDownload(req, res, note)
 | 
				
			||||||
            break;
 | 
					        break
 | 
				
			||||||
        case "info":
 | 
					      case 'info':
 | 
				
			||||||
            actionInfo(req, res, note);
 | 
					        actionInfo(req, res, note)
 | 
				
			||||||
            break;
 | 
					        break
 | 
				
			||||||
        case "pdf":
 | 
					      case 'pdf':
 | 
				
			||||||
            actionPDF(req, res, note);
 | 
					        actionPDF(req, res, note)
 | 
				
			||||||
            break;
 | 
					        break
 | 
				
			||||||
        case "gist":
 | 
					      case 'gist':
 | 
				
			||||||
            actionGist(req, res, note);
 | 
					        actionGist(req, res, note)
 | 
				
			||||||
            break;
 | 
					        break
 | 
				
			||||||
        case "revision":
 | 
					      case 'revision':
 | 
				
			||||||
            actionRevision(req, res, note);
 | 
					        actionRevision(req, res, note)
 | 
				
			||||||
            break;
 | 
					        break
 | 
				
			||||||
      default:
 | 
					      default:
 | 
				
			||||||
            return res.redirect(config.serverurl + '/' + noteId);
 | 
					        return res.redirect(config.serverurl + '/' + noteId)
 | 
				
			||||||
            break;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    });
 | 
					  })
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function publishNoteActions(req, res, next) {
 | 
					function publishNoteActions (req, res, next) {
 | 
				
			||||||
  findNote(req, res, function (note) {
 | 
					  findNote(req, res, function (note) {
 | 
				
			||||||
        var action = req.params.action;
 | 
					    var action = req.params.action
 | 
				
			||||||
    switch (action) {
 | 
					    switch (action) {
 | 
				
			||||||
        case "edit":
 | 
					      case 'edit':
 | 
				
			||||||
            res.redirect(config.serverurl + '/' + (note.alias ? note.alias : LZString.compressToBase64(note.id)));
 | 
					        res.redirect(config.serverurl + '/' + (note.alias ? note.alias : LZString.compressToBase64(note.id)))
 | 
				
			||||||
            break;
 | 
					        break
 | 
				
			||||||
      default:
 | 
					      default:
 | 
				
			||||||
            res.redirect(config.serverurl + '/s/' + note.shortid);
 | 
					        res.redirect(config.serverurl + '/s/' + note.shortid)
 | 
				
			||||||
            break;
 | 
					        break
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    });
 | 
					  })
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function publishSlideActions(req, res, next) {
 | 
					function publishSlideActions (req, res, next) {
 | 
				
			||||||
  findNote(req, res, function (note) {
 | 
					  findNote(req, res, function (note) {
 | 
				
			||||||
        var action = req.params.action;
 | 
					    var action = req.params.action
 | 
				
			||||||
    switch (action) {
 | 
					    switch (action) {
 | 
				
			||||||
        case "edit":
 | 
					      case 'edit':
 | 
				
			||||||
            res.redirect(config.serverurl + '/' + (note.alias ? note.alias : LZString.compressToBase64(note.id)));
 | 
					        res.redirect(config.serverurl + '/' + (note.alias ? note.alias : LZString.compressToBase64(note.id)))
 | 
				
			||||||
            break;
 | 
					        break
 | 
				
			||||||
      default:
 | 
					      default:
 | 
				
			||||||
            res.redirect(config.serverurl + '/p/' + note.shortid);
 | 
					        res.redirect(config.serverurl + '/p/' + note.shortid)
 | 
				
			||||||
            break;
 | 
					        break
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    });
 | 
					  })
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function githubActions(req, res, next) {
 | 
					function githubActions (req, res, next) {
 | 
				
			||||||
    var noteId = req.params.noteId;
 | 
					  var noteId = req.params.noteId
 | 
				
			||||||
  findNote(req, res, function (note) {
 | 
					  findNote(req, res, function (note) {
 | 
				
			||||||
        var action = req.params.action;
 | 
					    var action = req.params.action
 | 
				
			||||||
    switch (action) {
 | 
					    switch (action) {
 | 
				
			||||||
        case "gist":
 | 
					      case 'gist':
 | 
				
			||||||
            githubActionGist(req, res, note);
 | 
					        githubActionGist(req, res, note)
 | 
				
			||||||
            break;
 | 
					        break
 | 
				
			||||||
      default:
 | 
					      default:
 | 
				
			||||||
            res.redirect(config.serverurl + '/' + noteId);
 | 
					        res.redirect(config.serverurl + '/' + noteId)
 | 
				
			||||||
            break;
 | 
					        break
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    });
 | 
					  })
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function githubActionGist(req, res, note) {
 | 
					function githubActionGist (req, res, note) {
 | 
				
			||||||
    var code = req.query.code;
 | 
					  var code = req.query.code
 | 
				
			||||||
    var state = req.query.state;
 | 
					  var state = req.query.state
 | 
				
			||||||
  if (!code || !state) {
 | 
					  if (!code || !state) {
 | 
				
			||||||
        return response.errorForbidden(res);
 | 
					    return response.errorForbidden(res)
 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
    var data = {
 | 
					    var data = {
 | 
				
			||||||
      client_id: config.github.clientID,
 | 
					      client_id: config.github.clientID,
 | 
				
			||||||
@ -455,124 +449,122 @@ function githubActionGist(req, res, note) {
 | 
				
			|||||||
      code: code,
 | 
					      code: code,
 | 
				
			||||||
      state: state
 | 
					      state: state
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
        var auth_url = 'https://github.com/login/oauth/access_token';
 | 
					    var authUrl = 'https://github.com/login/oauth/access_token'
 | 
				
			||||||
    request({
 | 
					    request({
 | 
				
			||||||
                url: auth_url,
 | 
					      url: authUrl,
 | 
				
			||||||
                method: "POST",
 | 
					      method: 'POST',
 | 
				
			||||||
      json: data
 | 
					      json: data
 | 
				
			||||||
    }, function (error, httpResponse, body) {
 | 
					    }, function (error, httpResponse, body) {
 | 
				
			||||||
            if (!error && httpResponse.statusCode == 200) {
 | 
					      if (!error && httpResponse.statusCode === 200) {
 | 
				
			||||||
                var access_token = body.access_token;
 | 
					        var accessToken = body.access_token
 | 
				
			||||||
                if (access_token) {
 | 
					        if (accessToken) {
 | 
				
			||||||
                    var content = note.content;
 | 
					          var content = note.content
 | 
				
			||||||
                    var title = models.Note.decodeTitle(note.title);
 | 
					          var title = models.Note.decodeTitle(note.title)
 | 
				
			||||||
                    var filename = title.replace('/', ' ') + '.md';
 | 
					          var filename = title.replace('/', ' ') + '.md'
 | 
				
			||||||
          var gist = {
 | 
					          var gist = {
 | 
				
			||||||
                        "files": {}
 | 
					            'files': {}
 | 
				
			||||||
                    };
 | 
					          }
 | 
				
			||||||
          gist.files[filename] = {
 | 
					          gist.files[filename] = {
 | 
				
			||||||
                        "content": content
 | 
					            'content': content
 | 
				
			||||||
                    };
 | 
					          }
 | 
				
			||||||
                    var gist_url = "https://api.github.com/gists";
 | 
					          var gistUrl = 'https://api.github.com/gists'
 | 
				
			||||||
          request({
 | 
					          request({
 | 
				
			||||||
                        url: gist_url,
 | 
					            url: gistUrl,
 | 
				
			||||||
            headers: {
 | 
					            headers: {
 | 
				
			||||||
              'User-Agent': 'HackMD',
 | 
					              'User-Agent': 'HackMD',
 | 
				
			||||||
                            'Authorization': 'token ' + access_token
 | 
					              'Authorization': 'token ' + accessToken
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
                        method: "POST",
 | 
					            method: 'POST',
 | 
				
			||||||
            json: gist
 | 
					            json: gist
 | 
				
			||||||
          }, function (error, httpResponse, body) {
 | 
					          }, function (error, httpResponse, body) {
 | 
				
			||||||
                        if (!error && httpResponse.statusCode == 201) {
 | 
					            if (!error && httpResponse.statusCode === 201) {
 | 
				
			||||||
                            res.setHeader('referer', '');
 | 
					              res.setHeader('referer', '')
 | 
				
			||||||
                            res.redirect(body.html_url);
 | 
					              res.redirect(body.html_url)
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
                            return response.errorForbidden(res);
 | 
					              return response.errorForbidden(res)
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
                    });
 | 
					          })
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
                    return response.errorForbidden(res);
 | 
					          return response.errorForbidden(res)
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
                return response.errorForbidden(res);
 | 
					        return response.errorForbidden(res)
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function gitlabActions(req, res, next) {
 | 
					function gitlabActions (req, res, next) {
 | 
				
			||||||
    var noteId = req.params.noteId;
 | 
					  var noteId = req.params.noteId
 | 
				
			||||||
  findNote(req, res, function (note) {
 | 
					  findNote(req, res, function (note) {
 | 
				
			||||||
        var action = req.params.action;
 | 
					    var action = req.params.action
 | 
				
			||||||
    switch (action) {
 | 
					    switch (action) {
 | 
				
			||||||
        case "projects":
 | 
					      case 'projects':
 | 
				
			||||||
            gitlabActionProjects(req, res, note);
 | 
					        gitlabActionProjects(req, res, note)
 | 
				
			||||||
            break;
 | 
					        break
 | 
				
			||||||
      default:
 | 
					      default:
 | 
				
			||||||
            res.redirect(config.serverurl + '/' + noteId);
 | 
					        res.redirect(config.serverurl + '/' + noteId)
 | 
				
			||||||
            break;
 | 
					        break
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    });
 | 
					  })
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function gitlabActionProjects(req, res, note) {
 | 
					function gitlabActionProjects (req, res, note) {
 | 
				
			||||||
  if (req.isAuthenticated()) {
 | 
					  if (req.isAuthenticated()) {
 | 
				
			||||||
    models.User.findOne({
 | 
					    models.User.findOne({
 | 
				
			||||||
      where: {
 | 
					      where: {
 | 
				
			||||||
        id: req.user.id
 | 
					        id: req.user.id
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }).then(function (user) {
 | 
					    }).then(function (user) {
 | 
				
			||||||
            if (!user)
 | 
					      if (!user) { return response.errorNotFound(res) }
 | 
				
			||||||
                return response.errorNotFound(res);
 | 
					      var ret = { baseURL: config.gitlab.baseURL }
 | 
				
			||||||
            var ret = { baseURL: config.gitlab.baseURL };
 | 
					      ret.accesstoken = user.accessToken
 | 
				
			||||||
            ret.accesstoken = user.accessToken;
 | 
					      ret.profileid = user.profileid
 | 
				
			||||||
            ret.profileid = user.profileid;
 | 
					 | 
				
			||||||
      request(
 | 
					      request(
 | 
				
			||||||
                config.gitlab.baseURL + '/api/v3/projects?access_token=' + user.accessToken,
 | 
					                config.gitlab.baseURL + '/api/v3/projects?access_token=' + user.accessToken,
 | 
				
			||||||
                function(error, httpResponse, body) {
 | 
					                function (error, httpResponse, body) {
 | 
				
			||||||
                    if (!error && httpResponse.statusCode == 200) {
 | 
					                  if (!error && httpResponse.statusCode === 200) {
 | 
				
			||||||
                        ret.projects = JSON.parse(body);
 | 
					                    ret.projects = JSON.parse(body)
 | 
				
			||||||
                        return res.send(ret);
 | 
					                    return res.send(ret)
 | 
				
			||||||
                  } else {
 | 
					                  } else {
 | 
				
			||||||
                        return res.send(ret);
 | 
					                    return res.send(ret)
 | 
				
			||||||
                  }
 | 
					                  }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            );
 | 
					            )
 | 
				
			||||||
    }).catch(function (err) {
 | 
					    }).catch(function (err) {
 | 
				
			||||||
            logger.error('gitlab action projects failed: ' + err);
 | 
					      logger.error('gitlab action projects failed: ' + err)
 | 
				
			||||||
            return response.errorInternalError(res);
 | 
					      return response.errorInternalError(res)
 | 
				
			||||||
        });
 | 
					    })
 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
        return response.errorForbidden(res);
 | 
					    return response.errorForbidden(res)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function showPublishSlide(req, res, next) {
 | 
					function showPublishSlide (req, res, next) {
 | 
				
			||||||
  var include = [{
 | 
					  var include = [{
 | 
				
			||||||
    model: models.User,
 | 
					    model: models.User,
 | 
				
			||||||
        as: "owner"
 | 
					    as: 'owner'
 | 
				
			||||||
  }, {
 | 
					  }, {
 | 
				
			||||||
    model: models.User,
 | 
					    model: models.User,
 | 
				
			||||||
        as: "lastchangeuser"
 | 
					    as: 'lastchangeuser'
 | 
				
			||||||
    }];
 | 
					  }]
 | 
				
			||||||
  findNote(req, res, function (note) {
 | 
					  findNote(req, res, function (note) {
 | 
				
			||||||
    // force to use short id
 | 
					    // force to use short id
 | 
				
			||||||
        var shortid = req.params.shortid;
 | 
					    var shortid = req.params.shortid
 | 
				
			||||||
        if ((note.alias && shortid != note.alias) || (!note.alias && shortid != note.shortid))
 | 
					    if ((note.alias && shortid !== note.alias) || (!note.alias && shortid !== note.shortid)) { return res.redirect(config.serverurl + '/p/' + (note.alias || note.shortid)) }
 | 
				
			||||||
            return res.redirect(config.serverurl + "/p/" + (note.alias || note.shortid));
 | 
					 | 
				
			||||||
    note.increment('viewcount').then(function (note) {
 | 
					    note.increment('viewcount').then(function (note) {
 | 
				
			||||||
      if (!note) {
 | 
					      if (!note) {
 | 
				
			||||||
                return response.errorNotFound(res);
 | 
					        return response.errorNotFound(res)
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
            var body = note.content;
 | 
					      var body = note.content
 | 
				
			||||||
            var extracted = models.Note.extractMeta(body);
 | 
					      var extracted = models.Note.extractMeta(body)
 | 
				
			||||||
            markdown = extracted.markdown;
 | 
					      var markdown = extracted.markdown
 | 
				
			||||||
            meta = models.Note.parseMeta(extracted.meta);
 | 
					      var meta = models.Note.parseMeta(extracted.meta)
 | 
				
			||||||
            var createtime = note.createdAt;
 | 
					      var createtime = note.createdAt
 | 
				
			||||||
            var updatetime = note.lastchangeAt;
 | 
					      var updatetime = note.lastchangeAt
 | 
				
			||||||
            var title = models.Note.decodeTitle(note.title);
 | 
					      var title = models.Note.decodeTitle(note.title)
 | 
				
			||||||
            title = models.Note.generateWebTitle(meta.title || title);
 | 
					      title = models.Note.generateWebTitle(meta.title || title)
 | 
				
			||||||
            var origin = config.serverurl;
 | 
					      var origin = config.serverurl
 | 
				
			||||||
      var data = {
 | 
					      var data = {
 | 
				
			||||||
        title: title,
 | 
					        title: title,
 | 
				
			||||||
        description: meta.description || (markdown ? models.Note.generateDescription(markdown) : null),
 | 
					        description: meta.description || (markdown ? models.Note.generateDescription(markdown) : null),
 | 
				
			||||||
@ -587,23 +579,23 @@ function showPublishSlide(req, res, next) {
 | 
				
			|||||||
        ownerprofile: note.owner ? models.User.getProfile(note.owner) : null,
 | 
					        ownerprofile: note.owner ? models.User.getProfile(note.owner) : null,
 | 
				
			||||||
        lastchangeuser: note.lastchangeuser ? note.lastchangeuser.id : null,
 | 
					        lastchangeuser: note.lastchangeuser ? note.lastchangeuser.id : null,
 | 
				
			||||||
        lastchangeuserprofile: note.lastchangeuser ? models.User.getProfile(note.lastchangeuser) : null,
 | 
					        lastchangeuserprofile: note.lastchangeuser ? models.User.getProfile(note.lastchangeuser) : null,
 | 
				
			||||||
                robots: meta.robots || false, //default allow robots
 | 
					        robots: meta.robots || false, // default allow robots
 | 
				
			||||||
        GA: meta.GA,
 | 
					        GA: meta.GA,
 | 
				
			||||||
        disqus: meta.disqus
 | 
					        disqus: meta.disqus
 | 
				
			||||||
            };
 | 
					      }
 | 
				
			||||||
            return renderPublishSlide(data, res);
 | 
					      return renderPublishSlide(data, res)
 | 
				
			||||||
    }).catch(function (err) {
 | 
					    }).catch(function (err) {
 | 
				
			||||||
            logger.error(err);
 | 
					      logger.error(err)
 | 
				
			||||||
            return response.errorInternalError(res);
 | 
					      return response.errorInternalError(res)
 | 
				
			||||||
        });
 | 
					    })
 | 
				
			||||||
    }, include);
 | 
					  }, include)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function renderPublishSlide(data, res) {
 | 
					function renderPublishSlide (data, res) {
 | 
				
			||||||
  res.set({
 | 
					  res.set({
 | 
				
			||||||
    'Cache-Control': 'private' // only cache by client
 | 
					    'Cache-Control': 'private' // only cache by client
 | 
				
			||||||
    });
 | 
					  })
 | 
				
			||||||
    res.render(config.slidepath, data);
 | 
					  res.render(config.slidepath, data)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
module.exports = response;
 | 
					module.exports = response
 | 
				
			||||||
 | 
				
			|||||||
@ -1,140 +1,137 @@
 | 
				
			|||||||
// external modules
 | 
					// external modules
 | 
				
			||||||
var DiffMatchPatch = require('diff-match-patch');
 | 
					var DiffMatchPatch = require('diff-match-patch')
 | 
				
			||||||
var dmp = new DiffMatchPatch();
 | 
					var dmp = new DiffMatchPatch()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// core
 | 
					// core
 | 
				
			||||||
var config = require("../config.js");
 | 
					var config = require('../config.js')
 | 
				
			||||||
var logger = require("../logger.js");
 | 
					var logger = require('../logger.js')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
process.on('message', function(data) {
 | 
					process.on('message', function (data) {
 | 
				
			||||||
  if (!data || !data.msg || !data.cacheKey) {
 | 
					  if (!data || !data.msg || !data.cacheKey) {
 | 
				
			||||||
        return logger.error('dmp worker error: not enough data');
 | 
					    return logger.error('dmp worker error: not enough data')
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  switch (data.msg) {
 | 
					  switch (data.msg) {
 | 
				
			||||||
    case 'create patch':
 | 
					    case 'create patch':
 | 
				
			||||||
      if (!data.hasOwnProperty('lastDoc') || !data.hasOwnProperty('currDoc')) {
 | 
					      if (!data.hasOwnProperty('lastDoc') || !data.hasOwnProperty('currDoc')) {
 | 
				
			||||||
                return logger.error('dmp worker error: not enough data on create patch');
 | 
					        return logger.error('dmp worker error: not enough data on create patch')
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      try {
 | 
					      try {
 | 
				
			||||||
                var patch = createPatch(data.lastDoc, data.currDoc);
 | 
					        var patch = createPatch(data.lastDoc, data.currDoc)
 | 
				
			||||||
        process.send({
 | 
					        process.send({
 | 
				
			||||||
          msg: 'check',
 | 
					          msg: 'check',
 | 
				
			||||||
          result: patch,
 | 
					          result: patch,
 | 
				
			||||||
          cacheKey: data.cacheKey
 | 
					          cacheKey: data.cacheKey
 | 
				
			||||||
                });
 | 
					        })
 | 
				
			||||||
      } catch (err) {
 | 
					      } catch (err) {
 | 
				
			||||||
                logger.error('dmp worker error', err);
 | 
					        logger.error('dmp worker error', err)
 | 
				
			||||||
        process.send({
 | 
					        process.send({
 | 
				
			||||||
          msg: 'error',
 | 
					          msg: 'error',
 | 
				
			||||||
          error: err,
 | 
					          error: err,
 | 
				
			||||||
          cacheKey: data.cacheKey
 | 
					          cacheKey: data.cacheKey
 | 
				
			||||||
                });
 | 
					        })
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
            break;
 | 
					      break
 | 
				
			||||||
    case 'get revision':
 | 
					    case 'get revision':
 | 
				
			||||||
      if (!data.hasOwnProperty('revisions') || !data.hasOwnProperty('count')) {
 | 
					      if (!data.hasOwnProperty('revisions') || !data.hasOwnProperty('count')) {
 | 
				
			||||||
                return logger.error('dmp worker error: not enough data on get revision');
 | 
					        return logger.error('dmp worker error: not enough data on get revision')
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      try {
 | 
					      try {
 | 
				
			||||||
                var result = getRevision(data.revisions, data.count);
 | 
					        var result = getRevision(data.revisions, data.count)
 | 
				
			||||||
        process.send({
 | 
					        process.send({
 | 
				
			||||||
          msg: 'check',
 | 
					          msg: 'check',
 | 
				
			||||||
          result: result,
 | 
					          result: result,
 | 
				
			||||||
          cacheKey: data.cacheKey
 | 
					          cacheKey: data.cacheKey
 | 
				
			||||||
                });
 | 
					        })
 | 
				
			||||||
      } catch (err) {
 | 
					      } catch (err) {
 | 
				
			||||||
                logger.error('dmp worker error', err);
 | 
					        logger.error('dmp worker error', err)
 | 
				
			||||||
        process.send({
 | 
					        process.send({
 | 
				
			||||||
          msg: 'error',
 | 
					          msg: 'error',
 | 
				
			||||||
          error: err,
 | 
					          error: err,
 | 
				
			||||||
          cacheKey: data.cacheKey
 | 
					          cacheKey: data.cacheKey
 | 
				
			||||||
                });
 | 
					        })
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
            break;
 | 
					      break
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
});
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function createPatch(lastDoc, currDoc) {
 | 
					function createPatch (lastDoc, currDoc) {
 | 
				
			||||||
    var ms_start = (new Date()).getTime();
 | 
					  var msStart = (new Date()).getTime()
 | 
				
			||||||
    var diff = dmp.diff_main(lastDoc, currDoc);
 | 
					  var diff = dmp.diff_main(lastDoc, currDoc)
 | 
				
			||||||
    var patch = dmp.patch_make(lastDoc, diff);
 | 
					  var patch = dmp.patch_make(lastDoc, diff)
 | 
				
			||||||
    patch = dmp.patch_toText(patch);
 | 
					  patch = dmp.patch_toText(patch)
 | 
				
			||||||
    var ms_end = (new Date()).getTime();
 | 
					  var msEnd = (new Date()).getTime()
 | 
				
			||||||
  if (config.debug) {
 | 
					  if (config.debug) {
 | 
				
			||||||
        logger.info(patch);
 | 
					    logger.info(patch)
 | 
				
			||||||
        logger.info((ms_end - ms_start) + 'ms');
 | 
					    logger.info((msEnd - msStart) + 'ms')
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
    return patch;
 | 
					  return patch
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function getRevision(revisions, count) {
 | 
					function getRevision (revisions, count) {
 | 
				
			||||||
    var ms_start = (new Date()).getTime();
 | 
					  var msStart = (new Date()).getTime()
 | 
				
			||||||
    var startContent = null;
 | 
					  var startContent = null
 | 
				
			||||||
    var lastPatch = [];
 | 
					  var lastPatch = []
 | 
				
			||||||
    var applyPatches = [];
 | 
					  var applyPatches = []
 | 
				
			||||||
    var authorship = [];
 | 
					  var authorship = []
 | 
				
			||||||
  if (count <= Math.round(revisions.length / 2)) {
 | 
					  if (count <= Math.round(revisions.length / 2)) {
 | 
				
			||||||
    // start from top to target
 | 
					    // start from top to target
 | 
				
			||||||
        for (var i = 0; i < count; i++) {
 | 
					    for (let i = 0; i < count; i++) {
 | 
				
			||||||
            var revision = revisions[i];
 | 
					      let revision = revisions[i]
 | 
				
			||||||
            if (i == 0) {
 | 
					      if (i === 0) {
 | 
				
			||||||
                startContent = revision.content || revision.lastContent;
 | 
					        startContent = revision.content || revision.lastContent
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
            if (i != count - 1) {
 | 
					      if (i !== count - 1) {
 | 
				
			||||||
                var patch = dmp.patch_fromText(revision.patch);
 | 
					        let patch = dmp.patch_fromText(revision.patch)
 | 
				
			||||||
                applyPatches = applyPatches.concat(patch);
 | 
					        applyPatches = applyPatches.concat(patch)
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
            lastPatch = revision.patch;
 | 
					      lastPatch = revision.patch
 | 
				
			||||||
            authorship = revision.authorship;
 | 
					      authorship = revision.authorship
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    // swap DIFF_INSERT and DIFF_DELETE to achieve unpatching
 | 
					    // swap DIFF_INSERT and DIFF_DELETE to achieve unpatching
 | 
				
			||||||
        for (var i = 0, l = applyPatches.length; i < l; i++) {
 | 
					    for (let i = 0, l = applyPatches.length; i < l; i++) {
 | 
				
			||||||
            for (var j = 0, m = applyPatches[i].diffs.length; j < m; j++) {
 | 
					      for (let j = 0, m = applyPatches[i].diffs.length; j < m; j++) {
 | 
				
			||||||
                var diff = applyPatches[i].diffs[j];
 | 
					        var diff = applyPatches[i].diffs[j]
 | 
				
			||||||
                if (diff[0] == DiffMatchPatch.DIFF_INSERT)
 | 
					        if (diff[0] === DiffMatchPatch.DIFF_INSERT) { diff[0] = DiffMatchPatch.DIFF_DELETE } else if (diff[0] === DiffMatchPatch.DIFF_DELETE) { diff[0] = DiffMatchPatch.DIFF_INSERT }
 | 
				
			||||||
                    diff[0] = DiffMatchPatch.DIFF_DELETE;
 | 
					 | 
				
			||||||
                else if (diff[0] == DiffMatchPatch.DIFF_DELETE)
 | 
					 | 
				
			||||||
                    diff[0] = DiffMatchPatch.DIFF_INSERT;
 | 
					 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
    // start from bottom to target
 | 
					    // start from bottom to target
 | 
				
			||||||
        var l = revisions.length - 1;
 | 
					    var l = revisions.length - 1
 | 
				
			||||||
    for (var i = l; i >= count - 1; i--) {
 | 
					    for (var i = l; i >= count - 1; i--) {
 | 
				
			||||||
            var revision = revisions[i];
 | 
					      let revision = revisions[i]
 | 
				
			||||||
            if (i == l) {
 | 
					      if (i === l) {
 | 
				
			||||||
                startContent = revision.lastContent;
 | 
					        startContent = revision.lastContent
 | 
				
			||||||
                authorship = revision.authorship;
 | 
					        authorship = revision.authorship
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      if (revision.patch) {
 | 
					      if (revision.patch) {
 | 
				
			||||||
                var patch = dmp.patch_fromText(revision.patch);
 | 
					        let patch = dmp.patch_fromText(revision.patch)
 | 
				
			||||||
                applyPatches = applyPatches.concat(patch);
 | 
					        applyPatches = applyPatches.concat(patch)
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
            lastPatch = revision.patch;
 | 
					      lastPatch = revision.patch
 | 
				
			||||||
            authorship = revision.authorship;
 | 
					      authorship = revision.authorship
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  try {
 | 
					  try {
 | 
				
			||||||
        var finalContent = dmp.patch_apply(applyPatches, startContent)[0];
 | 
					    var finalContent = dmp.patch_apply(applyPatches, startContent)[0]
 | 
				
			||||||
  } catch (err) {
 | 
					  } catch (err) {
 | 
				
			||||||
        throw new Error(err);
 | 
					    throw new Error(err)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  var data = {
 | 
					  var data = {
 | 
				
			||||||
    content: finalContent,
 | 
					    content: finalContent,
 | 
				
			||||||
    patch: dmp.patch_fromText(lastPatch),
 | 
					    patch: dmp.patch_fromText(lastPatch),
 | 
				
			||||||
    authorship: authorship
 | 
					    authorship: authorship
 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
    var ms_end = (new Date()).getTime();
 | 
					 | 
				
			||||||
    if (config.debug) {
 | 
					 | 
				
			||||||
        logger.info((ms_end - ms_start) + 'ms');
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
    return data;
 | 
					  var msEnd = (new Date()).getTime()
 | 
				
			||||||
 | 
					  if (config.debug) {
 | 
				
			||||||
 | 
					    logger.info((msEnd - msStart) + 'ms')
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return data
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// log uncaught exception
 | 
					// log uncaught exception
 | 
				
			||||||
process.on('uncaughtException', function (err) {
 | 
					process.on('uncaughtException', function (err) {
 | 
				
			||||||
    logger.error('An uncaught exception has occured.');
 | 
					  logger.error('An uncaught exception has occured.')
 | 
				
			||||||
    logger.error(err);
 | 
					  logger.error(err)
 | 
				
			||||||
    logger.error('Process will exit now.');
 | 
					  logger.error('Process will exit now.')
 | 
				
			||||||
    process.exit(1);
 | 
					  process.exit(1)
 | 
				
			||||||
});
 | 
					})
 | 
				
			||||||
 | 
				
			|||||||
@ -5,7 +5,7 @@
 | 
				
			|||||||
  "main": "app.js",
 | 
					  "main": "app.js",
 | 
				
			||||||
  "license": "MIT",
 | 
					  "license": "MIT",
 | 
				
			||||||
  "scripts": {
 | 
					  "scripts": {
 | 
				
			||||||
    "test": "npm run-script lint",
 | 
					    "test": "node ./node_modules/standard/bin/cmd.js && npm run-script lint",
 | 
				
			||||||
    "lint": "eslint .",
 | 
					    "lint": "eslint .",
 | 
				
			||||||
    "dev": "webpack --config webpack.config.js --progress --colors --watch",
 | 
					    "dev": "webpack --config webpack.config.js --progress --colors --watch",
 | 
				
			||||||
    "build": "webpack --config webpack.production.js --progress --colors",
 | 
					    "build": "webpack --config webpack.production.js --progress --colors",
 | 
				
			||||||
@ -165,8 +165,15 @@
 | 
				
			|||||||
    "optimize-css-assets-webpack-plugin": "^1.3.0",
 | 
					    "optimize-css-assets-webpack-plugin": "^1.3.0",
 | 
				
			||||||
    "script-loader": "^0.7.0",
 | 
					    "script-loader": "^0.7.0",
 | 
				
			||||||
    "style-loader": "^0.13.1",
 | 
					    "style-loader": "^0.13.1",
 | 
				
			||||||
 | 
					    "standard": "^9.0.1",
 | 
				
			||||||
    "url-loader": "^0.5.7",
 | 
					    "url-loader": "^0.5.7",
 | 
				
			||||||
    "webpack": "^1.14.0",
 | 
					    "webpack": "^1.14.0",
 | 
				
			||||||
    "webpack-parallel-uglify-plugin": "^0.2.0"
 | 
					    "webpack-parallel-uglify-plugin": "^0.2.0"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "standard": {
 | 
				
			||||||
 | 
					    "ignore": [
 | 
				
			||||||
 | 
					      "lib/ot",
 | 
				
			||||||
 | 
					      "public/vendor"
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +1,10 @@
 | 
				
			|||||||
require('./locale');
 | 
					/* eslint-env browser, jquery */
 | 
				
			||||||
 | 
					/* global moment, serverurl */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
require('../css/cover.css');
 | 
					require('./locale')
 | 
				
			||||||
require('../css/site.css');
 | 
					
 | 
				
			||||||
 | 
					require('../css/cover.css')
 | 
				
			||||||
 | 
					require('../css/site.css')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
    checkIfAuth,
 | 
					    checkIfAuth,
 | 
				
			||||||
@ -9,7 +12,7 @@ import {
 | 
				
			|||||||
    getLoginState,
 | 
					    getLoginState,
 | 
				
			||||||
    resetCheckAuth,
 | 
					    resetCheckAuth,
 | 
				
			||||||
    setloginStateChangeEvent
 | 
					    setloginStateChangeEvent
 | 
				
			||||||
} from './lib/common/login';
 | 
					} from './lib/common/login'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
    clearDuplicatedHistory,
 | 
					    clearDuplicatedHistory,
 | 
				
			||||||
@ -23,411 +26,403 @@ import {
 | 
				
			|||||||
    removeHistory,
 | 
					    removeHistory,
 | 
				
			||||||
    saveHistory,
 | 
					    saveHistory,
 | 
				
			||||||
    saveStorageHistoryToServer
 | 
					    saveStorageHistoryToServer
 | 
				
			||||||
} from './history';
 | 
					} from './history'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { saveAs } from 'file-saver';
 | 
					import { saveAs } from 'file-saver'
 | 
				
			||||||
import List from 'list.js';
 | 
					import List from 'list.js'
 | 
				
			||||||
import S from 'string';
 | 
					import S from 'string'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const options = {
 | 
					const options = {
 | 
				
			||||||
  valueNames: ['id', 'text', 'timestamp', 'fromNow', 'time', 'tags', 'pinned'],
 | 
					  valueNames: ['id', 'text', 'timestamp', 'fromNow', 'time', 'tags', 'pinned'],
 | 
				
			||||||
    item: '<li class="col-xs-12 col-sm-6 col-md-6 col-lg-4">\
 | 
					  item: '<li class="col-xs-12 col-sm-6 col-md-6 col-lg-4">' +
 | 
				
			||||||
            <span class="id" style="display:none;"></span>\
 | 
					            '<span class="id" style="display:none;"></span>' +
 | 
				
			||||||
            <a href="#">\
 | 
					            '<a href="#">' +
 | 
				
			||||||
                <div class="item">\
 | 
					                '<div class="item">' +
 | 
				
			||||||
                    <div class="ui-history-pin fa fa-thumb-tack fa-fw"></div>\
 | 
					                    '<div class="ui-history-pin fa fa-thumb-tack fa-fw"></div>' +
 | 
				
			||||||
                    <div class="ui-history-close fa fa-close fa-fw" data-toggle="modal" data-target=".delete-modal"></div>\
 | 
					                    '<div class="ui-history-close fa fa-close fa-fw" data-toggle="modal" data-target=".delete-modal"></div>' +
 | 
				
			||||||
                    <div class="content">\
 | 
					                    '<div class="content">' +
 | 
				
			||||||
                        <h4 class="text"></h4>\
 | 
					                        '<h4 class="text"></h4>' +
 | 
				
			||||||
                        <p>\
 | 
					                        '<p>' +
 | 
				
			||||||
                            <i><i class="fa fa-clock-o"></i> visited </i><i class="fromNow"></i>\
 | 
					                            '<i><i class="fa fa-clock-o"></i> visited </i><i class="fromNow"></i>' +
 | 
				
			||||||
                            <br>\
 | 
					                            '<br>' +
 | 
				
			||||||
                            <i class="timestamp" style="display:none;"></i>\
 | 
					                            '<i class="timestamp" style="display:none;"></i>' +
 | 
				
			||||||
                            <i class="time"></i>\
 | 
					                            '<i class="time"></i>' +
 | 
				
			||||||
                        </p>\
 | 
					                        '</p>' +
 | 
				
			||||||
                        <p class="tags"></p>\
 | 
					                        '<p class="tags"></p>' +
 | 
				
			||||||
                    </div>\
 | 
					                    '</div>' +
 | 
				
			||||||
                </div>\
 | 
					                '</div>' +
 | 
				
			||||||
            </a>\
 | 
					            '</a>' +
 | 
				
			||||||
           </li>',
 | 
					           '</li>',
 | 
				
			||||||
  page: 18,
 | 
					  page: 18,
 | 
				
			||||||
  plugins: [
 | 
					  plugins: [
 | 
				
			||||||
        ListPagination({
 | 
					    window.ListPagination({
 | 
				
			||||||
      outerWindow: 1
 | 
					      outerWindow: 1
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
  ]
 | 
					  ]
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
const historyList = new List('history', options);
 | 
					const historyList = new List('history', options)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
migrateHistoryFromTempCallback = pageInit;
 | 
					window.migrateHistoryFromTempCallback = pageInit
 | 
				
			||||||
setloginStateChangeEvent(pageInit);
 | 
					setloginStateChangeEvent(pageInit)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pageInit();
 | 
					pageInit()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function pageInit() {
 | 
					function pageInit () {
 | 
				
			||||||
  checkIfAuth(
 | 
					  checkIfAuth(
 | 
				
			||||||
        data => {
 | 
					        data => {
 | 
				
			||||||
            $('.ui-signin').hide();
 | 
					          $('.ui-signin').hide()
 | 
				
			||||||
            $('.ui-or').hide();
 | 
					          $('.ui-or').hide()
 | 
				
			||||||
            $('.ui-welcome').show();
 | 
					          $('.ui-welcome').show()
 | 
				
			||||||
            if (data.photo) $('.ui-avatar').prop('src', data.photo).show();
 | 
					          if (data.photo) $('.ui-avatar').prop('src', data.photo).show()
 | 
				
			||||||
            else $('.ui-avatar').prop('src', '').hide();
 | 
					          else $('.ui-avatar').prop('src', '').hide()
 | 
				
			||||||
            $('.ui-name').html(data.name);
 | 
					          $('.ui-name').html(data.name)
 | 
				
			||||||
            $('.ui-signout').show();
 | 
					          $('.ui-signout').show()
 | 
				
			||||||
            $(".ui-history").click();
 | 
					          $('.ui-history').click()
 | 
				
			||||||
            parseServerToHistory(historyList, parseHistoryCallback);
 | 
					          parseServerToHistory(historyList, parseHistoryCallback)
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        () => {
 | 
					        () => {
 | 
				
			||||||
            $('.ui-signin').show();
 | 
					          $('.ui-signin').show()
 | 
				
			||||||
            $('.ui-or').show();
 | 
					          $('.ui-or').show()
 | 
				
			||||||
            $('.ui-welcome').hide();
 | 
					          $('.ui-welcome').hide()
 | 
				
			||||||
            $('.ui-avatar').prop('src', '').hide();
 | 
					          $('.ui-avatar').prop('src', '').hide()
 | 
				
			||||||
            $('.ui-name').html('');
 | 
					          $('.ui-name').html('')
 | 
				
			||||||
            $('.ui-signout').hide();
 | 
					          $('.ui-signout').hide()
 | 
				
			||||||
            parseStorageToHistory(historyList, parseHistoryCallback);
 | 
					          parseStorageToHistory(historyList, parseHistoryCallback)
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    );
 | 
					    )
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
$(".masthead-nav li").click(function () {
 | 
					$('.masthead-nav li').click(function () {
 | 
				
			||||||
    $(this).siblings().removeClass("active");
 | 
					  $(this).siblings().removeClass('active')
 | 
				
			||||||
    $(this).addClass("active");
 | 
					  $(this).addClass('active')
 | 
				
			||||||
});
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// prevent empty link change hash
 | 
					// prevent empty link change hash
 | 
				
			||||||
$('a[href="#"]').click(function (e) {
 | 
					$('a[href="#"]').click(function (e) {
 | 
				
			||||||
    e.preventDefault();
 | 
					  e.preventDefault()
 | 
				
			||||||
});
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
$(".ui-home").click(function (e) {
 | 
					$('.ui-home').click(function (e) {
 | 
				
			||||||
    if (!$("#home").is(':visible')) {
 | 
					  if (!$('#home').is(':visible')) {
 | 
				
			||||||
        $(".section:visible").hide();
 | 
					    $('.section:visible').hide()
 | 
				
			||||||
        $("#home").fadeIn();
 | 
					    $('#home').fadeIn()
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
});
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
$(".ui-history").click(() => {
 | 
					$('.ui-history').click(() => {
 | 
				
			||||||
    if (!$("#history").is(':visible')) {
 | 
					  if (!$('#history').is(':visible')) {
 | 
				
			||||||
        $(".section:visible").hide();
 | 
					    $('.section:visible').hide()
 | 
				
			||||||
        $("#history").fadeIn();
 | 
					    $('#history').fadeIn()
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
});
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function checkHistoryList() {
 | 
					function checkHistoryList () {
 | 
				
			||||||
    if ($("#history-list").children().length > 0) {
 | 
					  if ($('#history-list').children().length > 0) {
 | 
				
			||||||
        $('.pagination').show();
 | 
					    $('.pagination').show()
 | 
				
			||||||
        $(".ui-nohistory").hide();
 | 
					    $('.ui-nohistory').hide()
 | 
				
			||||||
        $(".ui-import-from-browser").hide();
 | 
					    $('.ui-import-from-browser').hide()
 | 
				
			||||||
    } else if ($("#history-list").children().length == 0) {
 | 
					  } else if ($('#history-list').children().length === 0) {
 | 
				
			||||||
        $('.pagination').hide();
 | 
					    $('.pagination').hide()
 | 
				
			||||||
        $(".ui-nohistory").slideDown();
 | 
					    $('.ui-nohistory').slideDown()
 | 
				
			||||||
    getStorageHistory(data => {
 | 
					    getStorageHistory(data => {
 | 
				
			||||||
            if (data && data.length > 0 && getLoginState() && historyList.items.length == 0) {
 | 
					      if (data && data.length > 0 && getLoginState() && historyList.items.length === 0) {
 | 
				
			||||||
                $(".ui-import-from-browser").slideDown();
 | 
					        $('.ui-import-from-browser').slideDown()
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
        });
 | 
					    })
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function parseHistoryCallback(list, notehistory) {
 | 
					function parseHistoryCallback (list, notehistory) {
 | 
				
			||||||
    checkHistoryList();
 | 
					  checkHistoryList()
 | 
				
			||||||
    //sort by pinned then timestamp
 | 
					    // sort by pinned then timestamp
 | 
				
			||||||
  list.sort('', {
 | 
					  list.sort('', {
 | 
				
			||||||
        sortFunction(a, b) {
 | 
					    sortFunction (a, b) {
 | 
				
			||||||
            const notea = a.values();
 | 
					      const notea = a.values()
 | 
				
			||||||
            const noteb = b.values();
 | 
					      const noteb = b.values()
 | 
				
			||||||
      if (notea.pinned && !noteb.pinned) {
 | 
					      if (notea.pinned && !noteb.pinned) {
 | 
				
			||||||
                return -1;
 | 
					        return -1
 | 
				
			||||||
      } else if (!notea.pinned && noteb.pinned) {
 | 
					      } else if (!notea.pinned && noteb.pinned) {
 | 
				
			||||||
                return 1;
 | 
					        return 1
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
        if (notea.timestamp > noteb.timestamp) {
 | 
					        if (notea.timestamp > noteb.timestamp) {
 | 
				
			||||||
                    return -1;
 | 
					          return -1
 | 
				
			||||||
        } else if (notea.timestamp < noteb.timestamp) {
 | 
					        } else if (notea.timestamp < noteb.timestamp) {
 | 
				
			||||||
                    return 1;
 | 
					          return 1
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
                    return 0;
 | 
					          return 0
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    });
 | 
					  })
 | 
				
			||||||
    // parse filter tags
 | 
					    // parse filter tags
 | 
				
			||||||
    const filtertags = [];
 | 
					  const filtertags = []
 | 
				
			||||||
  for (let i = 0, l = list.items.length; i < l; i++) {
 | 
					  for (let i = 0, l = list.items.length; i < l; i++) {
 | 
				
			||||||
        const tags = list.items[i]._values.tags;
 | 
					    const tags = list.items[i]._values.tags
 | 
				
			||||||
    if (tags && tags.length > 0) {
 | 
					    if (tags && tags.length > 0) {
 | 
				
			||||||
      for (let j = 0; j < tags.length; j++) {
 | 
					      for (let j = 0; j < tags.length; j++) {
 | 
				
			||||||
                //push info filtertags if not found
 | 
					                // push info filtertags if not found
 | 
				
			||||||
                let found = false;
 | 
					        let found = false
 | 
				
			||||||
                if (filtertags.includes(tags[j]))
 | 
					        if (filtertags.includes(tags[j])) { found = true }
 | 
				
			||||||
                    found = true;
 | 
					        if (!found) { filtertags.push(tags[j]) }
 | 
				
			||||||
                if (!found)
 | 
					 | 
				
			||||||
                    filtertags.push(tags[j]);
 | 
					 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
    buildTagsFilter(filtertags);
 | 
					  buildTagsFilter(filtertags)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// update items whenever list updated
 | 
					// update items whenever list updated
 | 
				
			||||||
historyList.on('updated', e => {
 | 
					historyList.on('updated', e => {
 | 
				
			||||||
  for (let i = 0, l = e.items.length; i < l; i++) {
 | 
					  for (let i = 0, l = e.items.length; i < l; i++) {
 | 
				
			||||||
        const item = e.items[i];
 | 
					    const item = e.items[i]
 | 
				
			||||||
    if (item.visible()) {
 | 
					    if (item.visible()) {
 | 
				
			||||||
            const itemEl = $(item.elm);
 | 
					      const itemEl = $(item.elm)
 | 
				
			||||||
            const values = item._values;
 | 
					      const values = item._values
 | 
				
			||||||
            const a = itemEl.find("a");
 | 
					      const a = itemEl.find('a')
 | 
				
			||||||
            const pin = itemEl.find(".ui-history-pin");
 | 
					      const pin = itemEl.find('.ui-history-pin')
 | 
				
			||||||
            const tagsEl = itemEl.find(".tags");
 | 
					      const tagsEl = itemEl.find('.tags')
 | 
				
			||||||
            //parse link to element a
 | 
					            // parse link to element a
 | 
				
			||||||
            a.attr('href', `${serverurl}/${values.id}`);
 | 
					      a.attr('href', `${serverurl}/${values.id}`)
 | 
				
			||||||
            //parse pinned
 | 
					            // parse pinned
 | 
				
			||||||
      if (values.pinned) {
 | 
					      if (values.pinned) {
 | 
				
			||||||
                pin.addClass('active');
 | 
					        pin.addClass('active')
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
                pin.removeClass('active');
 | 
					        pin.removeClass('active')
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
            //parse tags
 | 
					            // parse tags
 | 
				
			||||||
            const tags = values.tags;
 | 
					      const tags = values.tags
 | 
				
			||||||
      if (tags && tags.length > 0 && tagsEl.children().length <= 0) {
 | 
					      if (tags && tags.length > 0 && tagsEl.children().length <= 0) {
 | 
				
			||||||
                const labels = [];
 | 
					        const labels = []
 | 
				
			||||||
        for (let j = 0; j < tags.length; j++) {
 | 
					        for (let j = 0; j < tags.length; j++) {
 | 
				
			||||||
                    //push into the item label
 | 
					                    // push into the item label
 | 
				
			||||||
                    labels.push(`<span class='label label-default'>${tags[j]}</span>`);
 | 
					          labels.push(`<span class='label label-default'>${tags[j]}</span>`)
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
                tagsEl.html(labels.join(' '));
 | 
					        tagsEl.html(labels.join(' '))
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
    $(".ui-history-close").off('click');
 | 
					  $('.ui-history-close').off('click')
 | 
				
			||||||
    $(".ui-history-close").on('click', historyCloseClick);
 | 
					  $('.ui-history-close').on('click', historyCloseClick)
 | 
				
			||||||
    $(".ui-history-pin").off('click');
 | 
					  $('.ui-history-pin').off('click')
 | 
				
			||||||
    $(".ui-history-pin").on('click', historyPinClick);
 | 
					  $('.ui-history-pin').on('click', historyPinClick)
 | 
				
			||||||
});
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function historyCloseClick(e) {
 | 
					function historyCloseClick (e) {
 | 
				
			||||||
    e.preventDefault();
 | 
					  e.preventDefault()
 | 
				
			||||||
    const id = $(this).closest("a").siblings("span").html();
 | 
					  const id = $(this).closest('a').siblings('span').html()
 | 
				
			||||||
    const value = historyList.get('id', id)[0]._values;
 | 
					  const value = historyList.get('id', id)[0]._values
 | 
				
			||||||
    $('.ui-delete-modal-msg').text('Do you really want to delete below history?');
 | 
					  $('.ui-delete-modal-msg').text('Do you really want to delete below history?')
 | 
				
			||||||
    $('.ui-delete-modal-item').html(`<i class="fa fa-file-text"></i> ${value.text}<br><i class="fa fa-clock-o"></i> ${value.time}`);
 | 
					  $('.ui-delete-modal-item').html(`<i class="fa fa-file-text"></i> ${value.text}<br><i class="fa fa-clock-o"></i> ${value.time}`)
 | 
				
			||||||
    clearHistory = false;
 | 
					  clearHistory = false
 | 
				
			||||||
    deleteId = id;
 | 
					  deleteId = id
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function historyPinClick(e) {
 | 
					function historyPinClick (e) {
 | 
				
			||||||
    e.preventDefault();
 | 
					  e.preventDefault()
 | 
				
			||||||
    const $this = $(this);
 | 
					  const $this = $(this)
 | 
				
			||||||
    const id = $this.closest("a").siblings("span").html();
 | 
					  const id = $this.closest('a').siblings('span').html()
 | 
				
			||||||
    const item = historyList.get('id', id)[0];
 | 
					  const item = historyList.get('id', id)[0]
 | 
				
			||||||
    const values = item._values;
 | 
					  const values = item._values
 | 
				
			||||||
    let pinned = values.pinned;
 | 
					  let pinned = values.pinned
 | 
				
			||||||
  if (!values.pinned) {
 | 
					  if (!values.pinned) {
 | 
				
			||||||
        pinned = true;
 | 
					    pinned = true
 | 
				
			||||||
        item._values.pinned = true;
 | 
					    item._values.pinned = true
 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
        pinned = false;
 | 
					    pinned = false
 | 
				
			||||||
        item._values.pinned = false;
 | 
					    item._values.pinned = false
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  checkIfAuth(() => {
 | 
					  checkIfAuth(() => {
 | 
				
			||||||
    postHistoryToServer(id, {
 | 
					    postHistoryToServer(id, {
 | 
				
			||||||
      pinned
 | 
					      pinned
 | 
				
			||||||
    }, (err, result) => {
 | 
					    }, (err, result) => {
 | 
				
			||||||
      if (!err) {
 | 
					      if (!err) {
 | 
				
			||||||
                if (pinned)
 | 
					        if (pinned) { $this.addClass('active') } else { $this.removeClass('active') }
 | 
				
			||||||
                    $this.addClass('active');
 | 
					 | 
				
			||||||
                else
 | 
					 | 
				
			||||||
                    $this.removeClass('active');
 | 
					 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
        });
 | 
					    })
 | 
				
			||||||
  }, () => {
 | 
					  }, () => {
 | 
				
			||||||
    getHistory(notehistory => {
 | 
					    getHistory(notehistory => {
 | 
				
			||||||
            for(let i = 0; i < notehistory.length; i++) {
 | 
					      for (let i = 0; i < notehistory.length; i++) {
 | 
				
			||||||
                if (notehistory[i].id == id) {
 | 
					        if (notehistory[i].id === id) {
 | 
				
			||||||
                    notehistory[i].pinned = pinned;
 | 
					          notehistory[i].pinned = pinned
 | 
				
			||||||
                    break;
 | 
					          break
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
            saveHistory(notehistory);
 | 
					      saveHistory(notehistory)
 | 
				
			||||||
            if (pinned)
 | 
					      if (pinned) { $this.addClass('active') } else { $this.removeClass('active') }
 | 
				
			||||||
                $this.addClass('active');
 | 
					    })
 | 
				
			||||||
            else
 | 
					  })
 | 
				
			||||||
                $this.removeClass('active');
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//auto update item fromNow every minutes
 | 
					// auto update item fromNow every minutes
 | 
				
			||||||
setInterval(updateItemFromNow, 60000);
 | 
					setInterval(updateItemFromNow, 60000)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function updateItemFromNow() {
 | 
					function updateItemFromNow () {
 | 
				
			||||||
    const items = $('.item').toArray();
 | 
					  const items = $('.item').toArray()
 | 
				
			||||||
  for (let i = 0; i < items.length; i++) {
 | 
					  for (let i = 0; i < items.length; i++) {
 | 
				
			||||||
        const item = $(items[i]);
 | 
					    const item = $(items[i])
 | 
				
			||||||
        const timestamp = parseInt(item.find('.timestamp').text());
 | 
					    const timestamp = parseInt(item.find('.timestamp').text())
 | 
				
			||||||
        item.find('.fromNow').text(moment(timestamp).fromNow());
 | 
					    item.find('.fromNow').text(moment(timestamp).fromNow())
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var clearHistory = false;
 | 
					var clearHistory = false
 | 
				
			||||||
var deleteId = null;
 | 
					var deleteId = null
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function deleteHistory() {
 | 
					function deleteHistory () {
 | 
				
			||||||
  checkIfAuth(() => {
 | 
					  checkIfAuth(() => {
 | 
				
			||||||
    deleteServerHistory(deleteId, (err, result) => {
 | 
					    deleteServerHistory(deleteId, (err, result) => {
 | 
				
			||||||
      if (!err) {
 | 
					      if (!err) {
 | 
				
			||||||
        if (clearHistory) {
 | 
					        if (clearHistory) {
 | 
				
			||||||
                    historyList.clear();
 | 
					          historyList.clear()
 | 
				
			||||||
                    checkHistoryList();
 | 
					          checkHistoryList()
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
                    historyList.remove('id', deleteId);
 | 
					          historyList.remove('id', deleteId)
 | 
				
			||||||
                    checkHistoryList();
 | 
					          checkHistoryList()
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
            $('.delete-modal').modal('hide');
 | 
					      $('.delete-modal').modal('hide')
 | 
				
			||||||
            deleteId = null;
 | 
					      deleteId = null
 | 
				
			||||||
            clearHistory = false;
 | 
					      clearHistory = false
 | 
				
			||||||
        });
 | 
					    })
 | 
				
			||||||
  }, () => {
 | 
					  }, () => {
 | 
				
			||||||
    if (clearHistory) {
 | 
					    if (clearHistory) {
 | 
				
			||||||
            saveHistory([]);
 | 
					      saveHistory([])
 | 
				
			||||||
            historyList.clear();
 | 
					      historyList.clear()
 | 
				
			||||||
            checkHistoryList();
 | 
					      checkHistoryList()
 | 
				
			||||||
            deleteId = null;
 | 
					      deleteId = null
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
            if (!deleteId) return;
 | 
					      if (!deleteId) return
 | 
				
			||||||
      getHistory(notehistory => {
 | 
					      getHistory(notehistory => {
 | 
				
			||||||
                const newnotehistory = removeHistory(deleteId, notehistory);
 | 
					        const newnotehistory = removeHistory(deleteId, notehistory)
 | 
				
			||||||
                saveHistory(newnotehistory);
 | 
					        saveHistory(newnotehistory)
 | 
				
			||||||
                historyList.remove('id', deleteId);
 | 
					        historyList.remove('id', deleteId)
 | 
				
			||||||
                checkHistoryList();
 | 
					        checkHistoryList()
 | 
				
			||||||
                deleteId = null;
 | 
					        deleteId = null
 | 
				
			||||||
            });
 | 
					      })
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
        $('.delete-modal').modal('hide');
 | 
					    $('.delete-modal').modal('hide')
 | 
				
			||||||
        clearHistory = false;
 | 
					    clearHistory = false
 | 
				
			||||||
    });
 | 
					  })
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
$(".ui-delete-modal-confirm").click(() => {
 | 
					$('.ui-delete-modal-confirm').click(() => {
 | 
				
			||||||
    deleteHistory();
 | 
					  deleteHistory()
 | 
				
			||||||
});
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
$(".ui-import-from-browser").click(() => {
 | 
					$('.ui-import-from-browser').click(() => {
 | 
				
			||||||
  saveStorageHistoryToServer(() => {
 | 
					  saveStorageHistoryToServer(() => {
 | 
				
			||||||
        parseStorageToHistory(historyList, parseHistoryCallback);
 | 
					    parseStorageToHistory(historyList, parseHistoryCallback)
 | 
				
			||||||
    });
 | 
					  })
 | 
				
			||||||
});
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
$(".ui-save-history").click(() => {
 | 
					$('.ui-save-history').click(() => {
 | 
				
			||||||
  getHistory(data => {
 | 
					  getHistory(data => {
 | 
				
			||||||
        const history = JSON.stringify(data);
 | 
					    const history = JSON.stringify(data)
 | 
				
			||||||
    const blob = new Blob([history], {
 | 
					    const blob = new Blob([history], {
 | 
				
			||||||
            type: "application/json;charset=utf-8"
 | 
					      type: 'application/json;charset=utf-8'
 | 
				
			||||||
        });
 | 
					    })
 | 
				
			||||||
        saveAs(blob, `hackmd_history_${moment().format('YYYYMMDDHHmmss')}`, true);
 | 
					    saveAs(blob, `hackmd_history_${moment().format('YYYYMMDDHHmmss')}`, true)
 | 
				
			||||||
    });
 | 
					  })
 | 
				
			||||||
});
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
$(".ui-open-history").bind("change", e => {
 | 
					$('.ui-open-history').bind('change', e => {
 | 
				
			||||||
    const files = e.target.files || e.dataTransfer.files;
 | 
					  const files = e.target.files || e.dataTransfer.files
 | 
				
			||||||
    const file = files[0];
 | 
					  const file = files[0]
 | 
				
			||||||
    const reader = new FileReader();
 | 
					  const reader = new FileReader()
 | 
				
			||||||
  reader.onload = () => {
 | 
					  reader.onload = () => {
 | 
				
			||||||
        const notehistory = JSON.parse(reader.result);
 | 
					    const notehistory = JSON.parse(reader.result)
 | 
				
			||||||
        //console.log(notehistory);
 | 
					        // console.log(notehistory);
 | 
				
			||||||
        if (!reader.result) return;
 | 
					    if (!reader.result) return
 | 
				
			||||||
    getHistory(data => {
 | 
					    getHistory(data => {
 | 
				
			||||||
            let mergedata = data.concat(notehistory);
 | 
					      let mergedata = data.concat(notehistory)
 | 
				
			||||||
            mergedata = clearDuplicatedHistory(mergedata);
 | 
					      mergedata = clearDuplicatedHistory(mergedata)
 | 
				
			||||||
            saveHistory(mergedata);
 | 
					      saveHistory(mergedata)
 | 
				
			||||||
            parseHistory(historyList, parseHistoryCallback);
 | 
					      parseHistory(historyList, parseHistoryCallback)
 | 
				
			||||||
        });
 | 
					    })
 | 
				
			||||||
        $(".ui-open-history").replaceWith($(".ui-open-history").val('').clone(true));
 | 
					    $('.ui-open-history').replaceWith($('.ui-open-history').val('').clone(true))
 | 
				
			||||||
    };
 | 
					  }
 | 
				
			||||||
    reader.readAsText(file);
 | 
					  reader.readAsText(file)
 | 
				
			||||||
});
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
$(".ui-clear-history").click(() => {
 | 
					$('.ui-clear-history').click(() => {
 | 
				
			||||||
    $('.ui-delete-modal-msg').text('Do you really want to clear all history?');
 | 
					  $('.ui-delete-modal-msg').text('Do you really want to clear all history?')
 | 
				
			||||||
    $('.ui-delete-modal-item').html('There is no turning back.');
 | 
					  $('.ui-delete-modal-item').html('There is no turning back.')
 | 
				
			||||||
    clearHistory = true;
 | 
					  clearHistory = true
 | 
				
			||||||
    deleteId = null;
 | 
					  deleteId = null
 | 
				
			||||||
});
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
$(".ui-refresh-history").click(() => {
 | 
					$('.ui-refresh-history').click(() => {
 | 
				
			||||||
    const lastTags = $(".ui-use-tags").select2('val');
 | 
					  const lastTags = $('.ui-use-tags').select2('val')
 | 
				
			||||||
    $(".ui-use-tags").select2('val', '');
 | 
					  $('.ui-use-tags').select2('val', '')
 | 
				
			||||||
    historyList.filter();
 | 
					  historyList.filter()
 | 
				
			||||||
    const lastKeyword = $('.search').val();
 | 
					  const lastKeyword = $('.search').val()
 | 
				
			||||||
    $('.search').val('');
 | 
					  $('.search').val('')
 | 
				
			||||||
    historyList.search();
 | 
					  historyList.search()
 | 
				
			||||||
    $('#history-list').slideUp('fast');
 | 
					  $('#history-list').slideUp('fast')
 | 
				
			||||||
    $('.pagination').hide();
 | 
					  $('.pagination').hide()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    resetCheckAuth();
 | 
					  resetCheckAuth()
 | 
				
			||||||
    historyList.clear();
 | 
					  historyList.clear()
 | 
				
			||||||
  parseHistory(historyList, (list, notehistory) => {
 | 
					  parseHistory(historyList, (list, notehistory) => {
 | 
				
			||||||
        parseHistoryCallback(list, notehistory);
 | 
					    parseHistoryCallback(list, notehistory)
 | 
				
			||||||
        $(".ui-use-tags").select2('val', lastTags);
 | 
					    $('.ui-use-tags').select2('val', lastTags)
 | 
				
			||||||
        $(".ui-use-tags").trigger('change');
 | 
					    $('.ui-use-tags').trigger('change')
 | 
				
			||||||
        historyList.search(lastKeyword);
 | 
					    historyList.search(lastKeyword)
 | 
				
			||||||
        $('.search').val(lastKeyword);
 | 
					    $('.search').val(lastKeyword)
 | 
				
			||||||
        checkHistoryList();
 | 
					    checkHistoryList()
 | 
				
			||||||
        $('#history-list').slideDown('fast');
 | 
					    $('#history-list').slideDown('fast')
 | 
				
			||||||
    });
 | 
					  })
 | 
				
			||||||
});
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
$(".ui-logout").click(() => {
 | 
					$('.ui-logout').click(() => {
 | 
				
			||||||
    clearLoginState();
 | 
					  clearLoginState()
 | 
				
			||||||
    location.href = `${serverurl}/logout`;
 | 
					  location.href = `${serverurl}/logout`
 | 
				
			||||||
});
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
let filtertags = [];
 | 
					let filtertags = []
 | 
				
			||||||
$(".ui-use-tags").select2({
 | 
					$('.ui-use-tags').select2({
 | 
				
			||||||
    placeholder: $(".ui-use-tags").attr('placeholder'),
 | 
					  placeholder: $('.ui-use-tags').attr('placeholder'),
 | 
				
			||||||
  multiple: true,
 | 
					  multiple: true,
 | 
				
			||||||
    data() {
 | 
					  data () {
 | 
				
			||||||
    return {
 | 
					    return {
 | 
				
			||||||
      results: filtertags
 | 
					      results: filtertags
 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
});
 | 
					  }
 | 
				
			||||||
$('.select2-input').css('width', 'inherit');
 | 
					})
 | 
				
			||||||
buildTagsFilter([]);
 | 
					$('.select2-input').css('width', 'inherit')
 | 
				
			||||||
 | 
					buildTagsFilter([])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function buildTagsFilter(tags) {
 | 
					function buildTagsFilter (tags) {
 | 
				
			||||||
    for (let i = 0; i < tags.length; i++)
 | 
					  for (let i = 0; i < tags.length; i++) {
 | 
				
			||||||
    tags[i] = {
 | 
					    tags[i] = {
 | 
				
			||||||
      id: i,
 | 
					      id: i,
 | 
				
			||||||
      text: S(tags[i]).unescapeHTML().s
 | 
					      text: S(tags[i]).unescapeHTML().s
 | 
				
			||||||
        };
 | 
					    }
 | 
				
			||||||
    filtertags = tags;
 | 
					  }
 | 
				
			||||||
 | 
					  filtertags = tags
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
$(".ui-use-tags").on('change', function () {
 | 
					$('.ui-use-tags').on('change', function () {
 | 
				
			||||||
    const tags = [];
 | 
					  const tags = []
 | 
				
			||||||
    const data = $(this).select2('data');
 | 
					  const data = $(this).select2('data')
 | 
				
			||||||
    for (let i = 0; i < data.length; i++)
 | 
					  for (let i = 0; i < data.length; i++) { tags.push(data[i].text) }
 | 
				
			||||||
        tags.push(data[i].text);
 | 
					 | 
				
			||||||
  if (tags.length > 0) {
 | 
					  if (tags.length > 0) {
 | 
				
			||||||
    historyList.filter(item => {
 | 
					    historyList.filter(item => {
 | 
				
			||||||
            const values = item.values();
 | 
					      const values = item.values()
 | 
				
			||||||
            if (!values.tags) return false;
 | 
					      if (!values.tags) return false
 | 
				
			||||||
            let found = false;
 | 
					      let found = false
 | 
				
			||||||
      for (let i = 0; i < tags.length; i++) {
 | 
					      for (let i = 0; i < tags.length; i++) {
 | 
				
			||||||
        if (values.tags.includes(tags[i])) {
 | 
					        if (values.tags.includes(tags[i])) {
 | 
				
			||||||
                    found = true;
 | 
					          found = true
 | 
				
			||||||
                    break;
 | 
					          break
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
            return found;
 | 
					      return found
 | 
				
			||||||
        });
 | 
					    })
 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
        historyList.filter();
 | 
					    historyList.filter()
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
    checkHistoryList();
 | 
					  checkHistoryList()
 | 
				
			||||||
});
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
$('.search').keyup(() => {
 | 
					$('.search').keyup(() => {
 | 
				
			||||||
    checkHistoryList();
 | 
					  checkHistoryList()
 | 
				
			||||||
});
 | 
					})
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										1438
									
								
								public/js/extra.js
									
									
									
									
									
								
							
							
						
						
									
										1438
									
								
								public/js/extra.js
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@ -1,45 +1,45 @@
 | 
				
			|||||||
/**!
 | 
					/** !
 | 
				
			||||||
 * Google Drive File Picker Example
 | 
					 * Google Drive File Picker Example
 | 
				
			||||||
 * By Daniel Lo Nigro (http://dan.cx/)
 | 
					 * By Daniel Lo Nigro (http://dan.cx/)
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
(function() {
 | 
					(function () {
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
   * Initialise a Google Driver file picker
 | 
					   * Initialise a Google Driver file picker
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
	var FilePicker = window.FilePicker = function(options) {
 | 
					  var FilePicker = window.FilePicker = function (options) {
 | 
				
			||||||
    // Config
 | 
					    // Config
 | 
				
			||||||
		this.apiKey = options.apiKey;
 | 
					    this.apiKey = options.apiKey
 | 
				
			||||||
		this.clientId = options.clientId;
 | 
					    this.clientId = options.clientId
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Elements
 | 
					    // Elements
 | 
				
			||||||
		this.buttonEl = options.buttonEl;
 | 
					    this.buttonEl = options.buttonEl
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Events
 | 
					    // Events
 | 
				
			||||||
		this.onSelect = options.onSelect;
 | 
					    this.onSelect = options.onSelect
 | 
				
			||||||
		this.buttonEl.on('click', this.open.bind(this));		
 | 
					    this.buttonEl.on('click', this.open.bind(this))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Disable the button until the API loads, as it won't work properly until then.
 | 
					    // Disable the button until the API loads, as it won't work properly until then.
 | 
				
			||||||
		this.buttonEl.prop('disabled', true);
 | 
					    this.buttonEl.prop('disabled', true)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Load the drive API
 | 
					    // Load the drive API
 | 
				
			||||||
		gapi.client.setApiKey(this.apiKey);
 | 
					    window.gapi.client.setApiKey(this.apiKey)
 | 
				
			||||||
		gapi.client.load('drive', 'v2', this._driveApiLoaded.bind(this));
 | 
					    window.gapi.client.load('drive', 'v2', this._driveApiLoaded.bind(this))
 | 
				
			||||||
		google.load('picker', '1', { callback: this._pickerApiLoaded.bind(this) });
 | 
					    window.google.load('picker', '1', { callback: this._pickerApiLoaded.bind(this) })
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  FilePicker.prototype = {
 | 
					  FilePicker.prototype = {
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Open the file picker.
 | 
					     * Open the file picker.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
		open: function() {		
 | 
					    open: function () {
 | 
				
			||||||
      // Check if the user has already authenticated
 | 
					      // Check if the user has already authenticated
 | 
				
			||||||
			var token = gapi.auth.getToken();
 | 
					      var token = window.gapi.auth.getToken()
 | 
				
			||||||
      if (token) {
 | 
					      if (token) {
 | 
				
			||||||
				this._showPicker();
 | 
					        this._showPicker()
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
        // The user has not yet authenticated with Google
 | 
					        // The user has not yet authenticated with Google
 | 
				
			||||||
        // We need to do the authentication before displaying the Drive picker.
 | 
					        // We need to do the authentication before displaying the Drive picker.
 | 
				
			||||||
				this._doAuth(false, function() { this._showPicker(); }.bind(this));
 | 
					        this._doAuth(false, function () { this._showPicker() }.bind(this))
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -47,44 +47,43 @@
 | 
				
			|||||||
     * Show the file picker once authentication has been done.
 | 
					     * Show the file picker once authentication has been done.
 | 
				
			||||||
     * @private
 | 
					     * @private
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
		_showPicker: function() {
 | 
					    _showPicker: function () {
 | 
				
			||||||
			var accessToken = gapi.auth.getToken().access_token;
 | 
					      var accessToken = window.gapi.auth.getToken().access_token
 | 
				
			||||||
            var view = new google.picker.DocsView();
 | 
					      var view = new window.google.picker.DocsView()
 | 
				
			||||||
            view.setMimeTypes("text/markdown,text/html");
 | 
					      view.setMimeTypes('text/markdown,text/html')
 | 
				
			||||||
            view.setIncludeFolders(true);
 | 
					      view.setIncludeFolders(true)
 | 
				
			||||||
            view.setOwnedByMe(true);
 | 
					      view.setOwnedByMe(true)
 | 
				
			||||||
			this.picker = new google.picker.PickerBuilder().
 | 
					      this.picker = new window.google.picker.PickerBuilder()
 | 
				
			||||||
                enableFeature(google.picker.Feature.NAV_HIDDEN).
 | 
					                      .enableFeature(window.google.picker.Feature.NAV_HIDDEN)
 | 
				
			||||||
				addView(view).
 | 
					                      .addView(view)
 | 
				
			||||||
				setAppId(this.clientId).
 | 
					                      .setAppId(this.clientId)
 | 
				
			||||||
				setOAuthToken(accessToken).
 | 
					                      .setOAuthToken(accessToken)
 | 
				
			||||||
				setCallback(this._pickerCallback.bind(this)).
 | 
					                      .setCallback(this._pickerCallback.bind(this))
 | 
				
			||||||
				build().
 | 
					                      .build()
 | 
				
			||||||
				setVisible(true);
 | 
					                      .setVisible(true)
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Called when a file has been selected in the Google Drive file picker.
 | 
					     * Called when a file has been selected in the Google Drive file picker.
 | 
				
			||||||
     * @private
 | 
					     * @private
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
		_pickerCallback: function(data) {
 | 
					    _pickerCallback: function (data) {
 | 
				
			||||||
			if (data[google.picker.Response.ACTION] == google.picker.Action.PICKED) {
 | 
					      if (data[window.google.picker.Response.ACTION] === window.google.picker.Action.PICKED) {
 | 
				
			||||||
				var file = data[google.picker.Response.DOCUMENTS][0],
 | 
					        var file = data[window.google.picker.Response.DOCUMENTS][0]
 | 
				
			||||||
					id = file[google.picker.Document.ID],
 | 
					        var id = file[window.google.picker.Document.ID]
 | 
				
			||||||
					request = gapi.client.drive.files.get({
 | 
					        var request = window.gapi.client.drive.files.get({
 | 
				
			||||||
          fileId: id
 | 
					          fileId: id
 | 
				
			||||||
					});
 | 
					        })
 | 
				
			||||||
					
 | 
					        request.execute(this._fileGetCallback.bind(this))
 | 
				
			||||||
				request.execute(this._fileGetCallback.bind(this));
 | 
					 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Called when file details have been retrieved from Google Drive.
 | 
					     * Called when file details have been retrieved from Google Drive.
 | 
				
			||||||
     * @private
 | 
					     * @private
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
		_fileGetCallback: function(file) {
 | 
					    _fileGetCallback: function (file) {
 | 
				
			||||||
      if (this.onSelect) {
 | 
					      if (this.onSelect) {
 | 
				
			||||||
				this.onSelect(file);
 | 
					        this.onSelect(file)
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -92,28 +91,28 @@
 | 
				
			|||||||
     * Called when the Google Drive file picker API has finished loading.
 | 
					     * Called when the Google Drive file picker API has finished loading.
 | 
				
			||||||
     * @private
 | 
					     * @private
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
		_pickerApiLoaded: function() {
 | 
					    _pickerApiLoaded: function () {
 | 
				
			||||||
			this.buttonEl.prop('disabled', false);
 | 
					      this.buttonEl.prop('disabled', false)
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Called when the Google Drive API has finished loading.
 | 
					     * Called when the Google Drive API has finished loading.
 | 
				
			||||||
     * @private
 | 
					     * @private
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
		_driveApiLoaded: function() {
 | 
					    _driveApiLoaded: function () {
 | 
				
			||||||
			this._doAuth(true);
 | 
					      this._doAuth(true)
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Authenticate with Google Drive via the Google JavaScript API.
 | 
					     * Authenticate with Google Drive via the Google JavaScript API.
 | 
				
			||||||
     * @private
 | 
					     * @private
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
		_doAuth: function(immediate, callback) {	
 | 
					    _doAuth: function (immediate, callback) {
 | 
				
			||||||
			gapi.auth.authorize({
 | 
					      window.gapi.auth.authorize({
 | 
				
			||||||
        client_id: this.clientId,
 | 
					        client_id: this.clientId,
 | 
				
			||||||
        scope: 'https://www.googleapis.com/auth/drive.readonly',
 | 
					        scope: 'https://www.googleapis.com/auth/drive.readonly',
 | 
				
			||||||
        immediate: immediate
 | 
					        immediate: immediate
 | 
				
			||||||
			}, callback ? callback : function() {});
 | 
					      }, callback || function () {})
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
	};
 | 
					  }
 | 
				
			||||||
}());
 | 
					}())
 | 
				
			||||||
 | 
				
			|||||||
@ -1,30 +1,31 @@
 | 
				
			|||||||
 | 
					/* eslint-env browser, jquery */
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Helper for implementing retries with backoff. Initial retry
 | 
					 * Helper for implementing retries with backoff. Initial retry
 | 
				
			||||||
 * delay is 1 second, increasing by 2x (+jitter) for subsequent retries
 | 
					 * delay is 1 second, increasing by 2x (+jitter) for subsequent retries
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * @constructor
 | 
					 * @constructor
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
var RetryHandler = function() {
 | 
					var RetryHandler = function () {
 | 
				
			||||||
  this.interval = 1000; // Start at one second
 | 
					  this.interval = 1000 // Start at one second
 | 
				
			||||||
  this.maxInterval = 60 * 1000; // Don't wait longer than a minute 
 | 
					  this.maxInterval = 60 * 1000 // Don't wait longer than a minute
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Invoke the function after waiting
 | 
					 * Invoke the function after waiting
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * @param {function} fn Function to invoke
 | 
					 * @param {function} fn Function to invoke
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
RetryHandler.prototype.retry = function(fn) {
 | 
					RetryHandler.prototype.retry = function (fn) {
 | 
				
			||||||
  setTimeout(fn, this.interval);
 | 
					  setTimeout(fn, this.interval)
 | 
				
			||||||
  this.interval = this.nextInterval_();
 | 
					  this.interval = this.nextInterval_()
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Reset the counter (e.g. after successful request.)
 | 
					 * Reset the counter (e.g. after successful request.)
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
RetryHandler.prototype.reset = function() {
 | 
					RetryHandler.prototype.reset = function () {
 | 
				
			||||||
  this.interval = 1000;
 | 
					  this.interval = 1000
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Calculate the next wait time.
 | 
					 * Calculate the next wait time.
 | 
				
			||||||
@ -32,10 +33,10 @@ RetryHandler.prototype.reset = function() {
 | 
				
			|||||||
 *
 | 
					 *
 | 
				
			||||||
 * @private
 | 
					 * @private
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
RetryHandler.prototype.nextInterval_ = function() {
 | 
					RetryHandler.prototype.nextInterval_ = function () {
 | 
				
			||||||
  var interval = this.interval * 2 + this.getRandomInt_(0, 1000);
 | 
					  var interval = this.interval * 2 + this.getRandomInt_(0, 1000)
 | 
				
			||||||
  return Math.min(interval, this.maxInterval);
 | 
					  return Math.min(interval, this.maxInterval)
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Get a random int in the range of min to max. Used to add jitter to wait times.
 | 
					 * Get a random int in the range of min to max. Used to add jitter to wait times.
 | 
				
			||||||
@ -44,10 +45,9 @@ RetryHandler.prototype.nextInterval_ = function() {
 | 
				
			|||||||
 * @param {number} max Upper bounds
 | 
					 * @param {number} max Upper bounds
 | 
				
			||||||
 * @private
 | 
					 * @private
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
RetryHandler.prototype.getRandomInt_ = function(min, max) {
 | 
					RetryHandler.prototype.getRandomInt_ = function (min, max) {
 | 
				
			||||||
  return Math.floor(Math.random() * (max - min + 1) + min);
 | 
					  return Math.floor(Math.random() * (max - min + 1) + min)
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Helper class for resumable uploads using XHR/CORS. Can upload any Blob-like item, whether
 | 
					 * Helper class for resumable uploads using XHR/CORS. Can upload any Blob-like item, whether
 | 
				
			||||||
@ -75,116 +75,115 @@ RetryHandler.prototype.getRandomInt_ = function(min, max) {
 | 
				
			|||||||
 * @param {function} [options.onProgress] Callback for status for the in-progress upload
 | 
					 * @param {function} [options.onProgress] Callback for status for the in-progress upload
 | 
				
			||||||
 * @param {function} [options.onError] Callback if upload fails
 | 
					 * @param {function} [options.onError] Callback if upload fails
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
var MediaUploader = function(options) {
 | 
					var MediaUploader = function (options) {
 | 
				
			||||||
  var noop = function() {};
 | 
					  var noop = function () {}
 | 
				
			||||||
  this.file = options.file;
 | 
					  this.file = options.file
 | 
				
			||||||
  this.contentType = options.contentType || this.file.type || 'application/octet-stream';
 | 
					  this.contentType = options.contentType || this.file.type || 'application/octet-stream'
 | 
				
			||||||
  this.metadata = options.metadata || {
 | 
					  this.metadata = options.metadata || {
 | 
				
			||||||
    'title': this.file.name,
 | 
					    'title': this.file.name,
 | 
				
			||||||
    'mimeType': this.contentType
 | 
					    'mimeType': this.contentType
 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
  this.token = options.token;
 | 
					 | 
				
			||||||
  this.onComplete = options.onComplete || noop;
 | 
					 | 
				
			||||||
  this.onProgress = options.onProgress || noop;
 | 
					 | 
				
			||||||
  this.onError = options.onError || noop;
 | 
					 | 
				
			||||||
  this.offset = options.offset || 0;
 | 
					 | 
				
			||||||
  this.chunkSize = options.chunkSize || 0;
 | 
					 | 
				
			||||||
  this.retryHandler = new RetryHandler();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  this.url = options.url;
 | 
					 | 
				
			||||||
  if (!this.url) {
 | 
					 | 
				
			||||||
    var params = options.params || {};
 | 
					 | 
				
			||||||
    params.uploadType = 'resumable';
 | 
					 | 
				
			||||||
    this.url = this.buildUrl_(options.fileId, params, options.baseUrl);
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  this.httpMethod = options.fileId ? 'PUT' : 'POST';
 | 
					  this.token = options.token
 | 
				
			||||||
};
 | 
					  this.onComplete = options.onComplete || noop
 | 
				
			||||||
 | 
					  this.onProgress = options.onProgress || noop
 | 
				
			||||||
 | 
					  this.onError = options.onError || noop
 | 
				
			||||||
 | 
					  this.offset = options.offset || 0
 | 
				
			||||||
 | 
					  this.chunkSize = options.chunkSize || 0
 | 
				
			||||||
 | 
					  this.retryHandler = new RetryHandler()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  this.url = options.url
 | 
				
			||||||
 | 
					  if (!this.url) {
 | 
				
			||||||
 | 
					    var params = options.params || {}
 | 
				
			||||||
 | 
					    params.uploadType = 'resumable'
 | 
				
			||||||
 | 
					    this.url = this.buildUrl_(options.fileId, params, options.baseUrl)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  this.httpMethod = options.fileId ? 'PUT' : 'POST'
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Initiate the upload.
 | 
					 * Initiate the upload.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
MediaUploader.prototype.upload = function() {
 | 
					MediaUploader.prototype.upload = function () {
 | 
				
			||||||
  var self = this;
 | 
					  var xhr = new XMLHttpRequest()
 | 
				
			||||||
  var xhr = new XMLHttpRequest();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  xhr.open(this.httpMethod, this.url, true);
 | 
					  xhr.open(this.httpMethod, this.url, true)
 | 
				
			||||||
  xhr.setRequestHeader('Authorization', 'Bearer ' + this.token);
 | 
					  xhr.setRequestHeader('Authorization', 'Bearer ' + this.token)
 | 
				
			||||||
  xhr.setRequestHeader('Content-Type', 'application/json');
 | 
					  xhr.setRequestHeader('Content-Type', 'application/json')
 | 
				
			||||||
  xhr.setRequestHeader('X-Upload-Content-Length', this.file.size);
 | 
					  xhr.setRequestHeader('X-Upload-Content-Length', this.file.size)
 | 
				
			||||||
  xhr.setRequestHeader('X-Upload-Content-Type', this.contentType);
 | 
					  xhr.setRequestHeader('X-Upload-Content-Type', this.contentType)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  xhr.onload = function(e) {
 | 
					  xhr.onload = function (e) {
 | 
				
			||||||
    if (e.target.status < 400) {
 | 
					    if (e.target.status < 400) {
 | 
				
			||||||
      var location = e.target.getResponseHeader('Location');
 | 
					      var location = e.target.getResponseHeader('Location')
 | 
				
			||||||
      this.url = location;
 | 
					      this.url = location
 | 
				
			||||||
      this.sendFile_();
 | 
					      this.sendFile_()
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      this.onUploadError_(e);
 | 
					      this.onUploadError_(e)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }.bind(this);
 | 
					  }.bind(this)
 | 
				
			||||||
  xhr.onerror = this.onUploadError_.bind(this);
 | 
					  xhr.onerror = this.onUploadError_.bind(this)
 | 
				
			||||||
  xhr.send(JSON.stringify(this.metadata));
 | 
					  xhr.send(JSON.stringify(this.metadata))
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Send the actual file content.
 | 
					 * Send the actual file content.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * @private
 | 
					 * @private
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
MediaUploader.prototype.sendFile_ = function() {
 | 
					MediaUploader.prototype.sendFile_ = function () {
 | 
				
			||||||
  var content = this.file;
 | 
					  var content = this.file
 | 
				
			||||||
  var end = this.file.size;
 | 
					  var end = this.file.size
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (this.offset || this.chunkSize) {
 | 
					  if (this.offset || this.chunkSize) {
 | 
				
			||||||
    // Only bother to slice the file if we're either resuming or uploading in chunks
 | 
					    // Only bother to slice the file if we're either resuming or uploading in chunks
 | 
				
			||||||
    if (this.chunkSize) {
 | 
					    if (this.chunkSize) {
 | 
				
			||||||
      end = Math.min(this.offset + this.chunkSize, this.file.size);
 | 
					      end = Math.min(this.offset + this.chunkSize, this.file.size)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    content = content.slice(this.offset, end);
 | 
					    content = content.slice(this.offset, end)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  var xhr = new XMLHttpRequest();
 | 
					  var xhr = new XMLHttpRequest()
 | 
				
			||||||
  xhr.open('PUT', this.url, true);
 | 
					  xhr.open('PUT', this.url, true)
 | 
				
			||||||
  xhr.setRequestHeader('Content-Type', this.contentType);
 | 
					  xhr.setRequestHeader('Content-Type', this.contentType)
 | 
				
			||||||
  xhr.setRequestHeader('Content-Range', "bytes " + this.offset + "-" + (end - 1) + "/" + this.file.size);
 | 
					  xhr.setRequestHeader('Content-Range', 'bytes ' + this.offset + '-' + (end - 1) + '/' + this.file.size)
 | 
				
			||||||
  xhr.setRequestHeader('X-Upload-Content-Type', this.file.type);
 | 
					  xhr.setRequestHeader('X-Upload-Content-Type', this.file.type)
 | 
				
			||||||
  if (xhr.upload) {
 | 
					  if (xhr.upload) {
 | 
				
			||||||
    xhr.upload.addEventListener('progress', this.onProgress);
 | 
					    xhr.upload.addEventListener('progress', this.onProgress)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  xhr.onload = this.onContentUploadSuccess_.bind(this);
 | 
					  xhr.onload = this.onContentUploadSuccess_.bind(this)
 | 
				
			||||||
  xhr.onerror = this.onContentUploadError_.bind(this);
 | 
					  xhr.onerror = this.onContentUploadError_.bind(this)
 | 
				
			||||||
  xhr.send(content);
 | 
					  xhr.send(content)
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Query for the state of the file for resumption.
 | 
					 * Query for the state of the file for resumption.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * @private
 | 
					 * @private
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
MediaUploader.prototype.resume_ = function() {
 | 
					MediaUploader.prototype.resume_ = function () {
 | 
				
			||||||
  var xhr = new XMLHttpRequest();
 | 
					  var xhr = new XMLHttpRequest()
 | 
				
			||||||
  xhr.open('PUT', this.url, true);
 | 
					  xhr.open('PUT', this.url, true)
 | 
				
			||||||
  xhr.setRequestHeader('Content-Range', "bytes */" + this.file.size);
 | 
					  xhr.setRequestHeader('Content-Range', 'bytes */' + this.file.size)
 | 
				
			||||||
  xhr.setRequestHeader('X-Upload-Content-Type', this.file.type);
 | 
					  xhr.setRequestHeader('X-Upload-Content-Type', this.file.type)
 | 
				
			||||||
  if (xhr.upload) {
 | 
					  if (xhr.upload) {
 | 
				
			||||||
    xhr.upload.addEventListener('progress', this.onProgress);
 | 
					    xhr.upload.addEventListener('progress', this.onProgress)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  xhr.onload = this.onContentUploadSuccess_.bind(this);
 | 
					  xhr.onload = this.onContentUploadSuccess_.bind(this)
 | 
				
			||||||
  xhr.onerror = this.onContentUploadError_.bind(this);
 | 
					  xhr.onerror = this.onContentUploadError_.bind(this)
 | 
				
			||||||
  xhr.send();
 | 
					  xhr.send()
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Extract the last saved range if available in the request.
 | 
					 * Extract the last saved range if available in the request.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * @param {XMLHttpRequest} xhr Request object
 | 
					 * @param {XMLHttpRequest} xhr Request object
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
MediaUploader.prototype.extractRange_ = function(xhr) {
 | 
					MediaUploader.prototype.extractRange_ = function (xhr) {
 | 
				
			||||||
  var range = xhr.getResponseHeader('Range');
 | 
					  var range = xhr.getResponseHeader('Range')
 | 
				
			||||||
  if (range) {
 | 
					  if (range) {
 | 
				
			||||||
    this.offset = parseInt(range.match(/\d+/g).pop(), 10) + 1;
 | 
					    this.offset = parseInt(range.match(/\d+/g).pop(), 10) + 1
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Handle successful responses for uploads. Depending on the context,
 | 
					 * Handle successful responses for uploads. Depending on the context,
 | 
				
			||||||
@ -194,17 +193,17 @@ MediaUploader.prototype.extractRange_ = function(xhr) {
 | 
				
			|||||||
 * @private
 | 
					 * @private
 | 
				
			||||||
 * @param {object} e XHR event
 | 
					 * @param {object} e XHR event
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
MediaUploader.prototype.onContentUploadSuccess_ = function(e) {
 | 
					MediaUploader.prototype.onContentUploadSuccess_ = function (e) {
 | 
				
			||||||
  if (e.target.status == 200 || e.target.status == 201) {
 | 
					  if (e.target.status === 200 || e.target.status === 201) {
 | 
				
			||||||
    this.onComplete(e.target.response);
 | 
					    this.onComplete(e.target.response)
 | 
				
			||||||
  } else if (e.target.status == 308) {
 | 
					  } else if (e.target.status === 308) {
 | 
				
			||||||
    this.extractRange_(e.target);
 | 
					    this.extractRange_(e.target)
 | 
				
			||||||
    this.retryHandler.reset();
 | 
					    this.retryHandler.reset()
 | 
				
			||||||
    this.sendFile_();
 | 
					    this.sendFile_()
 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
    this.onContentUploadError_(e);
 | 
					    this.onContentUploadError_(e)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Handles errors for uploads. Either retries or aborts depending
 | 
					 * Handles errors for uploads. Either retries or aborts depending
 | 
				
			||||||
@ -213,13 +212,13 @@ MediaUploader.prototype.onContentUploadSuccess_ = function(e) {
 | 
				
			|||||||
 * @private
 | 
					 * @private
 | 
				
			||||||
 * @param {object} e XHR event
 | 
					 * @param {object} e XHR event
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
MediaUploader.prototype.onContentUploadError_ = function(e) {
 | 
					MediaUploader.prototype.onContentUploadError_ = function (e) {
 | 
				
			||||||
  if (e.target.status && e.target.status < 500) {
 | 
					  if (e.target.status && e.target.status < 500) {
 | 
				
			||||||
    this.onError(e.target.response);
 | 
					    this.onError(e.target.response)
 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
    this.retryHandler.retry(this.resume_.bind(this));
 | 
					    this.retryHandler.retry(this.resume_.bind(this))
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Handles errors for the initial request.
 | 
					 * Handles errors for the initial request.
 | 
				
			||||||
@ -227,9 +226,9 @@ MediaUploader.prototype.onContentUploadError_ = function(e) {
 | 
				
			|||||||
 * @private
 | 
					 * @private
 | 
				
			||||||
 * @param {object} e XHR event
 | 
					 * @param {object} e XHR event
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
MediaUploader.prototype.onUploadError_ = function(e) {
 | 
					MediaUploader.prototype.onUploadError_ = function (e) {
 | 
				
			||||||
  this.onError(e.target.response); // TODO - Retries for initial upload
 | 
					  this.onError(e.target.response) // TODO - Retries for initial upload
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Construct a query string from a hash/object
 | 
					 * Construct a query string from a hash/object
 | 
				
			||||||
@ -238,12 +237,12 @@ MediaUploader.prototype.onUploadError_ = function(e) {
 | 
				
			|||||||
 * @param {object} [params] Key/value pairs for query string
 | 
					 * @param {object} [params] Key/value pairs for query string
 | 
				
			||||||
 * @return {string} query string
 | 
					 * @return {string} query string
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
MediaUploader.prototype.buildQuery_ = function(params) {
 | 
					MediaUploader.prototype.buildQuery_ = function (params) {
 | 
				
			||||||
  params = params || {};
 | 
					  params = params || {}
 | 
				
			||||||
  return Object.keys(params).map(function(key) {
 | 
					  return Object.keys(params).map(function (key) {
 | 
				
			||||||
    return encodeURIComponent(key) + '=' + encodeURIComponent(params[key]);
 | 
					    return encodeURIComponent(key) + '=' + encodeURIComponent(params[key])
 | 
				
			||||||
  }).join('&');
 | 
					  }).join('&')
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Build the drive upload URL
 | 
					 * Build the drive upload URL
 | 
				
			||||||
@ -253,16 +252,16 @@ MediaUploader.prototype.buildQuery_ = function(params) {
 | 
				
			|||||||
 * @param {object} [params] Query parameters
 | 
					 * @param {object} [params] Query parameters
 | 
				
			||||||
 * @return {string} URL
 | 
					 * @return {string} URL
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
MediaUploader.prototype.buildUrl_ = function(id, params, baseUrl) {
 | 
					MediaUploader.prototype.buildUrl_ = function (id, params, baseUrl) {
 | 
				
			||||||
  var url = baseUrl || 'https://www.googleapis.com/upload/drive/v2/files/';
 | 
					  var url = baseUrl || 'https://www.googleapis.com/upload/drive/v2/files/'
 | 
				
			||||||
  if (id) {
 | 
					  if (id) {
 | 
				
			||||||
    url += id;
 | 
					    url += id
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  var query = this.buildQuery_(params);
 | 
					  var query = this.buildQuery_(params)
 | 
				
			||||||
  if (query) {
 | 
					  if (query) {
 | 
				
			||||||
    url += '?' + query;
 | 
					    url += '?' + query
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  return url;
 | 
					  return url
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
window.MediaUploader = MediaUploader;
 | 
					window.MediaUploader = MediaUploader
 | 
				
			||||||
 | 
				
			|||||||
@ -1,128 +1,113 @@
 | 
				
			|||||||
import store from 'store';
 | 
					/* eslint-env browser, jquery */
 | 
				
			||||||
import S from 'string';
 | 
					/* global serverurl, Cookies, moment */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import store from 'store'
 | 
				
			||||||
 | 
					import S from 'string'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
    checkIfAuth
 | 
					    checkIfAuth
 | 
				
			||||||
} from './lib/common/login';
 | 
					} from './lib/common/login'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
    urlpath
 | 
					    urlpath
 | 
				
			||||||
} from './lib/config';
 | 
					} from './lib/config'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
window.migrateHistoryFromTempCallback = null;
 | 
					window.migrateHistoryFromTempCallback = null
 | 
				
			||||||
 | 
					
 | 
				
			||||||
migrateHistoryFromTemp();
 | 
					migrateHistoryFromTemp()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function migrateHistoryFromTemp() {
 | 
					function migrateHistoryFromTemp () {
 | 
				
			||||||
    if (url('#tempid')) {
 | 
					  if (window.url('#tempid')) {
 | 
				
			||||||
    $.get(`${serverurl}/temp`, {
 | 
					    $.get(`${serverurl}/temp`, {
 | 
				
			||||||
                tempid: url('#tempid')
 | 
					      tempid: window.url('#tempid')
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
    .done(data => {
 | 
					    .done(data => {
 | 
				
			||||||
      if (data && data.temp) {
 | 
					      if (data && data.temp) {
 | 
				
			||||||
        getStorageHistory(olddata => {
 | 
					        getStorageHistory(olddata => {
 | 
				
			||||||
                        if (!olddata || olddata.length == 0) {
 | 
					          if (!olddata || olddata.length === 0) {
 | 
				
			||||||
                            saveHistoryToStorage(JSON.parse(data.temp));
 | 
					            saveHistoryToStorage(JSON.parse(data.temp))
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
                    });
 | 
					        })
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
    .always(() => {
 | 
					    .always(() => {
 | 
				
			||||||
                let hash = location.hash.split('#')[1];
 | 
					      let hash = location.hash.split('#')[1]
 | 
				
			||||||
                hash = hash.split('&');
 | 
					      hash = hash.split('&')
 | 
				
			||||||
                for (let i = 0; i < hash.length; i++)
 | 
					      for (let i = 0; i < hash.length; i++) {
 | 
				
			||||||
                    if (hash[i].indexOf('tempid') == 0) {
 | 
					        if (hash[i].indexOf('tempid') === 0) {
 | 
				
			||||||
                        hash.splice(i, 1);
 | 
					          hash.splice(i, 1)
 | 
				
			||||||
                        i--;
 | 
					          i--
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
                hash = hash.join('&');
 | 
					      }
 | 
				
			||||||
                location.hash = hash;
 | 
					      hash = hash.join('&')
 | 
				
			||||||
                if (migrateHistoryFromTempCallback)
 | 
					      location.hash = hash
 | 
				
			||||||
                    migrateHistoryFromTempCallback();
 | 
					      if (window.migrateHistoryFromTempCallback) { window.migrateHistoryFromTempCallback() }
 | 
				
			||||||
            });
 | 
					    })
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function saveHistory(notehistory) {
 | 
					export function saveHistory (notehistory) {
 | 
				
			||||||
  checkIfAuth(
 | 
					  checkIfAuth(
 | 
				
			||||||
        () => {
 | 
					        () => {
 | 
				
			||||||
            saveHistoryToServer(notehistory);
 | 
					          saveHistoryToServer(notehistory)
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        () => {
 | 
					        () => {
 | 
				
			||||||
            saveHistoryToStorage(notehistory);
 | 
					          saveHistoryToStorage(notehistory)
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    );
 | 
					    )
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function saveHistoryToStorage(notehistory) {
 | 
					function saveHistoryToStorage (notehistory) {
 | 
				
			||||||
    if (store.enabled)
 | 
					  if (store.enabled) { store.set('notehistory', JSON.stringify(notehistory)) } else { saveHistoryToCookie(notehistory) }
 | 
				
			||||||
        store.set('notehistory', JSON.stringify(notehistory));
 | 
					 | 
				
			||||||
    else
 | 
					 | 
				
			||||||
        saveHistoryToCookie(notehistory);
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function saveHistoryToCookie(notehistory) {
 | 
					function saveHistoryToCookie (notehistory) {
 | 
				
			||||||
  Cookies.set('notehistory', notehistory, {
 | 
					  Cookies.set('notehistory', notehistory, {
 | 
				
			||||||
    expires: 365
 | 
					    expires: 365
 | 
				
			||||||
    });
 | 
					  })
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function saveHistoryToServer(notehistory) {
 | 
					function saveHistoryToServer (notehistory) {
 | 
				
			||||||
  $.post(`${serverurl}/history`, {
 | 
					  $.post(`${serverurl}/history`, {
 | 
				
			||||||
    history: JSON.stringify(notehistory)
 | 
					    history: JSON.stringify(notehistory)
 | 
				
			||||||
    });
 | 
					  })
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function saveCookieHistoryToStorage(callback) {
 | 
					export function saveStorageHistoryToServer (callback) {
 | 
				
			||||||
    store.set('notehistory', Cookies.get('notehistory'));
 | 
					  const data = store.get('notehistory')
 | 
				
			||||||
    callback();
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export function saveStorageHistoryToServer(callback) {
 | 
					 | 
				
			||||||
    const data = store.get('notehistory');
 | 
					 | 
				
			||||||
  if (data) {
 | 
					  if (data) {
 | 
				
			||||||
    $.post(`${serverurl}/history`, {
 | 
					    $.post(`${serverurl}/history`, {
 | 
				
			||||||
      history: data
 | 
					      history: data
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
            .done(data => {
 | 
					            .done(data => {
 | 
				
			||||||
                callback(data);
 | 
					              callback(data)
 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function saveCookieHistoryToServer(callback) {
 | 
					 | 
				
			||||||
    $.post(`${serverurl}/history`, {
 | 
					 | 
				
			||||||
            history: Cookies.get('notehistory')
 | 
					 | 
				
			||||||
            })
 | 
					            })
 | 
				
			||||||
        .done(data => {
 | 
					  }
 | 
				
			||||||
            callback(data);
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function clearDuplicatedHistory(notehistory) {
 | 
					export function clearDuplicatedHistory (notehistory) {
 | 
				
			||||||
    const newnotehistory = [];
 | 
					  const newnotehistory = []
 | 
				
			||||||
  for (let i = 0; i < notehistory.length; i++) {
 | 
					  for (let i = 0; i < notehistory.length; i++) {
 | 
				
			||||||
        let found = false;
 | 
					    let found = false
 | 
				
			||||||
    for (let j = 0; j < newnotehistory.length; j++) {
 | 
					    for (let j = 0; j < newnotehistory.length; j++) {
 | 
				
			||||||
            const id = notehistory[i].id.replace(/\=+$/, '');
 | 
					      const id = notehistory[i].id.replace(/=+$/, '')
 | 
				
			||||||
            const newId = newnotehistory[j].id.replace(/\=+$/, '');
 | 
					      const newId = newnotehistory[j].id.replace(/=+$/, '')
 | 
				
			||||||
            if (id == newId || notehistory[i].id == newnotehistory[j].id || !notehistory[i].id || !newnotehistory[j].id) {
 | 
					      if (id === newId || notehistory[i].id === newnotehistory[j].id || !notehistory[i].id || !newnotehistory[j].id) {
 | 
				
			||||||
                const time = (typeof notehistory[i].time === 'number' ? moment(notehistory[i].time) : moment(notehistory[i].time, 'MMMM Do YYYY, h:mm:ss a'));
 | 
					        const time = (typeof notehistory[i].time === 'number' ? moment(notehistory[i].time) : moment(notehistory[i].time, 'MMMM Do YYYY, h:mm:ss a'))
 | 
				
			||||||
                const newTime = (typeof newnotehistory[i].time === 'number' ? moment(newnotehistory[i].time) : moment(newnotehistory[i].time, 'MMMM Do YYYY, h:mm:ss a'));
 | 
					        const newTime = (typeof newnotehistory[i].time === 'number' ? moment(newnotehistory[i].time) : moment(newnotehistory[i].time, 'MMMM Do YYYY, h:mm:ss a'))
 | 
				
			||||||
                if(time >= newTime) {
 | 
					        if (time >= newTime) {
 | 
				
			||||||
                    newnotehistory[j] = notehistory[i];
 | 
					          newnotehistory[j] = notehistory[i]
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
                found = true;
 | 
					        found = true
 | 
				
			||||||
                break;
 | 
					        break
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
        if (!found)
 | 
					    if (!found) { newnotehistory.push(notehistory[i]) }
 | 
				
			||||||
            newnotehistory.push(notehistory[i]);
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
    return newnotehistory;
 | 
					  return newnotehistory
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function addHistory(id, text, time, tags, pinned, notehistory) {
 | 
					function addHistory (id, text, time, tags, pinned, notehistory) {
 | 
				
			||||||
    // only add when note id exists
 | 
					    // only add when note id exists
 | 
				
			||||||
  if (id) {
 | 
					  if (id) {
 | 
				
			||||||
    notehistory.push({
 | 
					    notehistory.push({
 | 
				
			||||||
@ -131,242 +116,213 @@ function addHistory(id, text, time, tags, pinned, notehistory) {
 | 
				
			|||||||
      time,
 | 
					      time,
 | 
				
			||||||
      tags,
 | 
					      tags,
 | 
				
			||||||
      pinned
 | 
					      pinned
 | 
				
			||||||
      });
 | 
					    })
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
    return notehistory;
 | 
					  return notehistory
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function removeHistory(id, notehistory) {
 | 
					export function removeHistory (id, notehistory) {
 | 
				
			||||||
  for (let i = 0; i < notehistory.length; i++) {
 | 
					  for (let i = 0; i < notehistory.length; i++) {
 | 
				
			||||||
        if (notehistory[i].id == id) {
 | 
					    if (notehistory[i].id === id) {
 | 
				
			||||||
            notehistory.splice(i, 1);
 | 
					      notehistory.splice(i, 1)
 | 
				
			||||||
            i -= 1;
 | 
					      i -= 1
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
    return notehistory;
 | 
					  return notehistory
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//used for inner
 | 
					// used for inner
 | 
				
			||||||
export function writeHistory(title, tags) {
 | 
					export function writeHistory (title, tags) {
 | 
				
			||||||
  checkIfAuth(
 | 
					  checkIfAuth(
 | 
				
			||||||
        () => {
 | 
					        () => {
 | 
				
			||||||
            // no need to do this anymore, this will count from server-side
 | 
					            // no need to do this anymore, this will count from server-side
 | 
				
			||||||
            // writeHistoryToServer(title, tags);
 | 
					            // writeHistoryToServer(title, tags);
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        () => {
 | 
					        () => {
 | 
				
			||||||
            writeHistoryToStorage(title, tags);
 | 
					          writeHistoryToStorage(title, tags)
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    );
 | 
					    )
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function writeHistoryToServer(title, tags) {
 | 
					function writeHistoryToCookie (title, tags) {
 | 
				
			||||||
    $.get(`${serverurl}/history`)
 | 
					  var notehistory
 | 
				
			||||||
        .done(data => {
 | 
					 | 
				
			||||||
  try {
 | 
					  try {
 | 
				
			||||||
                if (data.history) {
 | 
					    notehistory = Cookies.getJSON('notehistory')
 | 
				
			||||||
                    var notehistory = data.history;
 | 
					 | 
				
			||||||
                } else {
 | 
					 | 
				
			||||||
                    var notehistory = [];
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
  } catch (err) {
 | 
					  } catch (err) {
 | 
				
			||||||
                var notehistory = [];
 | 
					    notehistory = []
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
            if (!notehistory)
 | 
					  if (!notehistory) { notehistory = [] }
 | 
				
			||||||
                notehistory = [];
 | 
					  const newnotehistory = generateHistory(title, tags, notehistory)
 | 
				
			||||||
 | 
					  saveHistoryToCookie(newnotehistory)
 | 
				
			||||||
            const newnotehistory = generateHistory(title, tags, notehistory);
 | 
					 | 
				
			||||||
            saveHistoryToServer(newnotehistory);
 | 
					 | 
				
			||||||
        })
 | 
					 | 
				
			||||||
        .fail((xhr, status, error) => {
 | 
					 | 
				
			||||||
            console.error(xhr.responseText);
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function writeHistoryToCookie(title, tags) {
 | 
					function writeHistoryToStorage (title, tags) {
 | 
				
			||||||
    try {
 | 
					 | 
				
			||||||
        var notehistory = Cookies.getJSON('notehistory');
 | 
					 | 
				
			||||||
    } catch (err) {
 | 
					 | 
				
			||||||
        var notehistory = [];
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    if (!notehistory)
 | 
					 | 
				
			||||||
        notehistory = [];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const newnotehistory = generateHistory(title, tags, notehistory);
 | 
					 | 
				
			||||||
    saveHistoryToCookie(newnotehistory);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function writeHistoryToStorage(title, tags) {
 | 
					 | 
				
			||||||
  if (store.enabled) {
 | 
					  if (store.enabled) {
 | 
				
			||||||
        let data = store.get('notehistory');
 | 
					    let data = store.get('notehistory')
 | 
				
			||||||
 | 
					    var notehistory
 | 
				
			||||||
    if (data) {
 | 
					    if (data) {
 | 
				
			||||||
            if (typeof data == "string")
 | 
					      if (typeof data === 'string') { data = JSON.parse(data) }
 | 
				
			||||||
                data = JSON.parse(data);
 | 
					      notehistory = data
 | 
				
			||||||
            var notehistory = data;
 | 
					 | 
				
			||||||
        } else
 | 
					 | 
				
			||||||
            var notehistory = [];
 | 
					 | 
				
			||||||
        if (!notehistory)
 | 
					 | 
				
			||||||
            notehistory = [];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        const newnotehistory = generateHistory(title, tags, notehistory);
 | 
					 | 
				
			||||||
        saveHistoryToStorage(newnotehistory);
 | 
					 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
        writeHistoryToCookie(title, tags);
 | 
					      notehistory = []
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (!notehistory) { notehistory = [] }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const newnotehistory = generateHistory(title, tags, notehistory)
 | 
				
			||||||
 | 
					    saveHistoryToStorage(newnotehistory)
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    writeHistoryToCookie(title, tags)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if (!Array.isArray) {
 | 
					if (!Array.isArray) {
 | 
				
			||||||
    Array.isArray = arg => Object.prototype.toString.call(arg) === '[object Array]';
 | 
					  Array.isArray = arg => Object.prototype.toString.call(arg) === '[object Array]'
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function renderHistory(title, tags) {
 | 
					function renderHistory (title, tags) {
 | 
				
			||||||
    //console.debug(tags);
 | 
					    // console.debug(tags);
 | 
				
			||||||
    const id = urlpath ? location.pathname.slice(urlpath.length + 1, location.pathname.length).split('/')[1] : location.pathname.split('/')[1];
 | 
					  const id = urlpath ? location.pathname.slice(urlpath.length + 1, location.pathname.length).split('/')[1] : location.pathname.split('/')[1]
 | 
				
			||||||
  return {
 | 
					  return {
 | 
				
			||||||
    id,
 | 
					    id,
 | 
				
			||||||
    text: title,
 | 
					    text: title,
 | 
				
			||||||
    time: moment().valueOf(),
 | 
					    time: moment().valueOf(),
 | 
				
			||||||
    tags
 | 
					    tags
 | 
				
			||||||
    };
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function generateHistory(title, tags, notehistory) {
 | 
					function generateHistory (title, tags, notehistory) {
 | 
				
			||||||
    const info = renderHistory(title, tags);
 | 
					  const info = renderHistory(title, tags)
 | 
				
			||||||
    //keep any pinned data
 | 
					    // keep any pinned data
 | 
				
			||||||
    let pinned = false;
 | 
					  let pinned = false
 | 
				
			||||||
  for (let i = 0; i < notehistory.length; i++) {
 | 
					  for (let i = 0; i < notehistory.length; i++) {
 | 
				
			||||||
        if (notehistory[i].id == info.id && notehistory[i].pinned) {
 | 
					    if (notehistory[i].id === info.id && notehistory[i].pinned) {
 | 
				
			||||||
            pinned = true;
 | 
					      pinned = true
 | 
				
			||||||
            break;
 | 
					      break
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
    notehistory = removeHistory(info.id, notehistory);
 | 
					  notehistory = removeHistory(info.id, notehistory)
 | 
				
			||||||
    notehistory = addHistory(info.id, info.text, info.time, info.tags, pinned, notehistory);
 | 
					  notehistory = addHistory(info.id, info.text, info.time, info.tags, pinned, notehistory)
 | 
				
			||||||
    notehistory = clearDuplicatedHistory(notehistory);
 | 
					  notehistory = clearDuplicatedHistory(notehistory)
 | 
				
			||||||
    return notehistory;
 | 
					  return notehistory
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//used for outer
 | 
					// used for outer
 | 
				
			||||||
export function getHistory(callback) {
 | 
					export function getHistory (callback) {
 | 
				
			||||||
  checkIfAuth(
 | 
					  checkIfAuth(
 | 
				
			||||||
        () => {
 | 
					        () => {
 | 
				
			||||||
            getServerHistory(callback);
 | 
					          getServerHistory(callback)
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        () => {
 | 
					        () => {
 | 
				
			||||||
            getStorageHistory(callback);
 | 
					          getStorageHistory(callback)
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    );
 | 
					    )
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function getServerHistory(callback) {
 | 
					function getServerHistory (callback) {
 | 
				
			||||||
  $.get(`${serverurl}/history`)
 | 
					  $.get(`${serverurl}/history`)
 | 
				
			||||||
        .done(data => {
 | 
					        .done(data => {
 | 
				
			||||||
          if (data.history) {
 | 
					          if (data.history) {
 | 
				
			||||||
                callback(data.history);
 | 
					            callback(data.history)
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
        .fail((xhr, status, error) => {
 | 
					        .fail((xhr, status, error) => {
 | 
				
			||||||
            console.error(xhr.responseText);
 | 
					          console.error(xhr.responseText)
 | 
				
			||||||
        });
 | 
					        })
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function getCookieHistory(callback) {
 | 
					function getCookieHistory (callback) {
 | 
				
			||||||
    callback(Cookies.getJSON('notehistory'));
 | 
					  callback(Cookies.getJSON('notehistory'))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function getStorageHistory(callback) {
 | 
					export function getStorageHistory (callback) {
 | 
				
			||||||
  if (store.enabled) {
 | 
					  if (store.enabled) {
 | 
				
			||||||
        let data = store.get('notehistory');
 | 
					    let data = store.get('notehistory')
 | 
				
			||||||
    if (data) {
 | 
					    if (data) {
 | 
				
			||||||
            if (typeof data == "string")
 | 
					      if (typeof data === 'string') { data = JSON.parse(data) }
 | 
				
			||||||
                data = JSON.parse(data);
 | 
					      callback(data)
 | 
				
			||||||
            callback(data);
 | 
					    } else { getCookieHistory(callback) }
 | 
				
			||||||
        } else
 | 
					 | 
				
			||||||
            getCookieHistory(callback);
 | 
					 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
        getCookieHistory(callback);
 | 
					    getCookieHistory(callback)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function parseHistory(list, callback) {
 | 
					export function parseHistory (list, callback) {
 | 
				
			||||||
  checkIfAuth(
 | 
					  checkIfAuth(
 | 
				
			||||||
        () => {
 | 
					        () => {
 | 
				
			||||||
            parseServerToHistory(list, callback);
 | 
					          parseServerToHistory(list, callback)
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        () => {
 | 
					        () => {
 | 
				
			||||||
            parseStorageToHistory(list, callback);
 | 
					          parseStorageToHistory(list, callback)
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    );
 | 
					    )
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function parseServerToHistory(list, callback) {
 | 
					export function parseServerToHistory (list, callback) {
 | 
				
			||||||
  $.get(`${serverurl}/history`)
 | 
					  $.get(`${serverurl}/history`)
 | 
				
			||||||
        .done(data => {
 | 
					        .done(data => {
 | 
				
			||||||
          if (data.history) {
 | 
					          if (data.history) {
 | 
				
			||||||
                parseToHistory(list, data.history, callback);
 | 
					            parseToHistory(list, data.history, callback)
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
        .fail((xhr, status, error) => {
 | 
					        .fail((xhr, status, error) => {
 | 
				
			||||||
            console.error(xhr.responseText);
 | 
					          console.error(xhr.responseText)
 | 
				
			||||||
        });
 | 
					        })
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function parseCookieToHistory(list, callback) {
 | 
					function parseCookieToHistory (list, callback) {
 | 
				
			||||||
    const notehistory = Cookies.getJSON('notehistory');
 | 
					  const notehistory = Cookies.getJSON('notehistory')
 | 
				
			||||||
    parseToHistory(list, notehistory, callback);
 | 
					  parseToHistory(list, notehistory, callback)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function parseStorageToHistory(list, callback) {
 | 
					export function parseStorageToHistory (list, callback) {
 | 
				
			||||||
  if (store.enabled) {
 | 
					  if (store.enabled) {
 | 
				
			||||||
        let data = store.get('notehistory');
 | 
					    let data = store.get('notehistory')
 | 
				
			||||||
    if (data) {
 | 
					    if (data) {
 | 
				
			||||||
            if (typeof data == "string")
 | 
					      if (typeof data === 'string') { data = JSON.parse(data) }
 | 
				
			||||||
                data = JSON.parse(data);
 | 
					      parseToHistory(list, data, callback)
 | 
				
			||||||
            parseToHistory(list, data, callback);
 | 
					    } else { parseCookieToHistory(list, callback) }
 | 
				
			||||||
        } else
 | 
					 | 
				
			||||||
            parseCookieToHistory(list, callback);
 | 
					 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
        parseCookieToHistory(list, callback);
 | 
					    parseCookieToHistory(list, callback)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function parseToHistory(list, notehistory, callback) {
 | 
					function parseToHistory (list, notehistory, callback) {
 | 
				
			||||||
    if (!callback) return;
 | 
					  if (!callback) return
 | 
				
			||||||
    else if (!list || !notehistory) callback(list, notehistory);
 | 
					  else if (!list || !notehistory) callback(list, notehistory)
 | 
				
			||||||
  else if (notehistory && notehistory.length > 0) {
 | 
					  else if (notehistory && notehistory.length > 0) {
 | 
				
			||||||
    for (let i = 0; i < notehistory.length; i++) {
 | 
					    for (let i = 0; i < notehistory.length; i++) {
 | 
				
			||||||
            //parse time to timestamp and fromNow
 | 
					            // parse time to timestamp and fromNow
 | 
				
			||||||
            const timestamp = (typeof notehistory[i].time === 'number' ? moment(notehistory[i].time) : moment(notehistory[i].time, 'MMMM Do YYYY, h:mm:ss a'));
 | 
					      const timestamp = (typeof notehistory[i].time === 'number' ? moment(notehistory[i].time) : moment(notehistory[i].time, 'MMMM Do YYYY, h:mm:ss a'))
 | 
				
			||||||
            notehistory[i].timestamp = timestamp.valueOf();
 | 
					      notehistory[i].timestamp = timestamp.valueOf()
 | 
				
			||||||
            notehistory[i].fromNow = timestamp.fromNow();
 | 
					      notehistory[i].fromNow = timestamp.fromNow()
 | 
				
			||||||
            notehistory[i].time = timestamp.format('llll');
 | 
					      notehistory[i].time = timestamp.format('llll')
 | 
				
			||||||
            // prevent XSS
 | 
					            // prevent XSS
 | 
				
			||||||
            notehistory[i].text = S(notehistory[i].text).escapeHTML().s;
 | 
					      notehistory[i].text = S(notehistory[i].text).escapeHTML().s
 | 
				
			||||||
            notehistory[i].tags = (notehistory[i].tags && notehistory[i].tags.length > 0) ? S(notehistory[i].tags).escapeHTML().s.split(',') : [];
 | 
					      notehistory[i].tags = (notehistory[i].tags && notehistory[i].tags.length > 0) ? S(notehistory[i].tags).escapeHTML().s.split(',') : []
 | 
				
			||||||
            // add to list
 | 
					            // add to list
 | 
				
			||||||
            if (notehistory[i].id && list.get('id', notehistory[i].id).length == 0)
 | 
					      if (notehistory[i].id && list.get('id', notehistory[i].id).length === 0) { list.add(notehistory[i]) }
 | 
				
			||||||
                list.add(notehistory[i]);
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
    callback(list, notehistory);
 | 
					  callback(list, notehistory)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function postHistoryToServer(noteId, data, callback) {
 | 
					export function postHistoryToServer (noteId, data, callback) {
 | 
				
			||||||
  $.post(`${serverurl}/history/${noteId}`, data)
 | 
					  $.post(`${serverurl}/history/${noteId}`, data)
 | 
				
			||||||
    .done(result => callback(null, result))
 | 
					    .done(result => callback(null, result))
 | 
				
			||||||
    .fail((xhr, status, error) => {
 | 
					    .fail((xhr, status, error) => {
 | 
				
			||||||
        console.error(xhr.responseText);
 | 
					      console.error(xhr.responseText)
 | 
				
			||||||
        return callback(error, null);
 | 
					      return callback(error, null)
 | 
				
			||||||
    });
 | 
					    })
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function deleteServerHistory(noteId, callback) {
 | 
					export function deleteServerHistory (noteId, callback) {
 | 
				
			||||||
  $.ajax({
 | 
					  $.ajax({
 | 
				
			||||||
        url: `${serverurl}/history${noteId ? '/' + noteId : ""}`,
 | 
					    url: `${serverurl}/history${noteId ? '/' + noteId : ''}`,
 | 
				
			||||||
    type: 'DELETE'
 | 
					    type: 'DELETE'
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
    .done(result => callback(null, result))
 | 
					    .done(result => callback(null, result))
 | 
				
			||||||
    .fail((xhr, status, error) => {
 | 
					    .fail((xhr, status, error) => {
 | 
				
			||||||
        console.error(xhr.responseText);
 | 
					      console.error(xhr.responseText)
 | 
				
			||||||
        return callback(error, null);
 | 
					      return callback(error, null)
 | 
				
			||||||
    });
 | 
					    })
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,6 @@
 | 
				
			|||||||
require('../css/github-extract.css');
 | 
					require('../css/github-extract.css')
 | 
				
			||||||
require('../css/markdown.css');
 | 
					require('../css/markdown.css')
 | 
				
			||||||
require('../css/extra.css');
 | 
					require('../css/extra.css')
 | 
				
			||||||
require('../css/slide-preview.css');
 | 
					require('../css/slide-preview.css')
 | 
				
			||||||
require('../css/google-font.css');
 | 
					require('../css/google-font.css')
 | 
				
			||||||
require('../css/site.css');
 | 
					require('../css/site.css')
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										4789
									
								
								public/js/index.js
									
									
									
									
									
								
							
							
						
						
									
										4789
									
								
								public/js/index.js
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@ -1,82 +1,85 @@
 | 
				
			|||||||
import { serverurl } from '../config';
 | 
					/* eslint-env browser, jquery */
 | 
				
			||||||
 | 
					/* global Cookies */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
let checkAuth = false;
 | 
					import { serverurl } from '../config'
 | 
				
			||||||
let profile = null;
 | 
					 | 
				
			||||||
let lastLoginState = getLoginState();
 | 
					 | 
				
			||||||
let lastUserId = getUserId();
 | 
					 | 
				
			||||||
var loginStateChangeEvent = null;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function setloginStateChangeEvent(func) {
 | 
					let checkAuth = false
 | 
				
			||||||
    loginStateChangeEvent = func;
 | 
					let profile = null
 | 
				
			||||||
 | 
					let lastLoginState = getLoginState()
 | 
				
			||||||
 | 
					let lastUserId = getUserId()
 | 
				
			||||||
 | 
					var loginStateChangeEvent = null
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function setloginStateChangeEvent (func) {
 | 
				
			||||||
 | 
					  loginStateChangeEvent = func
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function resetCheckAuth() {
 | 
					export function resetCheckAuth () {
 | 
				
			||||||
    checkAuth = false;
 | 
					  checkAuth = false
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function setLoginState(bool, id) {
 | 
					export function setLoginState (bool, id) {
 | 
				
			||||||
  Cookies.set('loginstate', bool, {
 | 
					  Cookies.set('loginstate', bool, {
 | 
				
			||||||
    expires: 365
 | 
					    expires: 365
 | 
				
			||||||
    });
 | 
					  })
 | 
				
			||||||
  if (id) {
 | 
					  if (id) {
 | 
				
			||||||
    Cookies.set('userid', id, {
 | 
					    Cookies.set('userid', id, {
 | 
				
			||||||
      expires: 365
 | 
					      expires: 365
 | 
				
			||||||
        });
 | 
					    })
 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
        Cookies.remove('userid');
 | 
					    Cookies.remove('userid')
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
    lastLoginState = bool;
 | 
					  lastLoginState = bool
 | 
				
			||||||
    lastUserId = id;
 | 
					  lastUserId = id
 | 
				
			||||||
    checkLoginStateChanged();
 | 
					  checkLoginStateChanged()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function checkLoginStateChanged() {
 | 
					export function checkLoginStateChanged () {
 | 
				
			||||||
    if (getLoginState() != lastLoginState || getUserId() != lastUserId) {
 | 
					  if (getLoginState() !== lastLoginState || getUserId() !== lastUserId) {
 | 
				
			||||||
        if (loginStateChangeEvent) setTimeout(loginStateChangeEvent, 100);
 | 
					    if (loginStateChangeEvent) setTimeout(loginStateChangeEvent, 100)
 | 
				
			||||||
        return true;
 | 
					    return true
 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
        return false;
 | 
					    return false
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function getLoginState() {
 | 
					export function getLoginState () {
 | 
				
			||||||
    const state = Cookies.get('loginstate');
 | 
					  const state = Cookies.get('loginstate')
 | 
				
			||||||
    return state === "true" || state === true;
 | 
					  return state === 'true' || state === true
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function getUserId() {
 | 
					export function getUserId () {
 | 
				
			||||||
    return Cookies.get('userid');
 | 
					  return Cookies.get('userid')
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function clearLoginState() {
 | 
					export function clearLoginState () {
 | 
				
			||||||
    Cookies.remove('loginstate');
 | 
					  Cookies.remove('loginstate')
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function checkIfAuth(yesCallback, noCallback) {
 | 
					export function checkIfAuth (yesCallback, noCallback) {
 | 
				
			||||||
    const cookieLoginState = getLoginState();
 | 
					  const cookieLoginState = getLoginState()
 | 
				
			||||||
    if (checkLoginStateChanged()) checkAuth = false;
 | 
					  if (checkLoginStateChanged()) checkAuth = false
 | 
				
			||||||
    if (!checkAuth || typeof cookieLoginState == 'undefined') {
 | 
					  if (!checkAuth || typeof cookieLoginState === 'undefined') {
 | 
				
			||||||
    $.get(`${serverurl}/me`)
 | 
					    $.get(`${serverurl}/me`)
 | 
				
			||||||
            .done(data => {
 | 
					            .done(data => {
 | 
				
			||||||
                if (data && data.status == 'ok') {
 | 
					              if (data && data.status === 'ok') {
 | 
				
			||||||
                    profile = data;
 | 
					                profile = data
 | 
				
			||||||
                    yesCallback(profile);
 | 
					                yesCallback(profile)
 | 
				
			||||||
                    setLoginState(true, data.id);
 | 
					                setLoginState(true, data.id)
 | 
				
			||||||
              } else {
 | 
					              } else {
 | 
				
			||||||
                    noCallback();
 | 
					                noCallback()
 | 
				
			||||||
                    setLoginState(false);
 | 
					                setLoginState(false)
 | 
				
			||||||
              }
 | 
					              }
 | 
				
			||||||
            })
 | 
					            })
 | 
				
			||||||
            .fail(() => {
 | 
					            .fail(() => {
 | 
				
			||||||
                noCallback();
 | 
					              noCallback()
 | 
				
			||||||
            })
 | 
					            })
 | 
				
			||||||
            .always(() => {
 | 
					            .always(() => {
 | 
				
			||||||
                checkAuth = true;
 | 
					              checkAuth = true
 | 
				
			||||||
            });
 | 
					            })
 | 
				
			||||||
  } else if (cookieLoginState) {
 | 
					  } else if (cookieLoginState) {
 | 
				
			||||||
        yesCallback(profile);
 | 
					    yesCallback(profile)
 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
        noCallback();
 | 
					    noCallback()
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -86,4 +89,4 @@ export default {
 | 
				
			|||||||
  lastLoginState,
 | 
					  lastLoginState,
 | 
				
			||||||
  lastUserId,
 | 
					  lastUserId,
 | 
				
			||||||
  loginStateChangeEvent
 | 
					  loginStateChangeEvent
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,19 +1,19 @@
 | 
				
			|||||||
import configJson from '../../../../config.json'; // root path json config
 | 
					import configJson from '../../../../config.json' // root path json config
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const config = 'production' === process.env.NODE_ENV ? configJson.production : configJson.development;
 | 
					const config = process.env.NODE_ENV === 'production' ? configJson.production : configJson.development
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const GOOGLE_API_KEY = (config.google && config.google.apiKey) || '';
 | 
					export const GOOGLE_API_KEY = (config.google && config.google.apiKey) || ''
 | 
				
			||||||
export const GOOGLE_CLIENT_ID = (config.google && config.google.clientID) || '';
 | 
					export const GOOGLE_CLIENT_ID = (config.google && config.google.clientID) || ''
 | 
				
			||||||
export const DROPBOX_APP_KEY = (config.dropbox && config.dropbox.appKey) || '';
 | 
					export const DROPBOX_APP_KEY = (config.dropbox && config.dropbox.appKey) || ''
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const domain = config.domain || ''; // domain name
 | 
					export const domain = config.domain || '' // domain name
 | 
				
			||||||
export const urlpath = config.urlpath || ''; // sub url path, like: www.example.com/<urlpath>
 | 
					export const urlpath = config.urlpath || '' // sub url path, like: www.example.com/<urlpath>
 | 
				
			||||||
export const debug = config.debug || false;
 | 
					export const debug = config.debug || false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const port = window.location.port;
 | 
					export const port = window.location.port
 | 
				
			||||||
export const serverurl = `${window.location.protocol}//${domain ? domain : window.location.hostname}${port ? ':' + port : ''}${urlpath ? '/' + urlpath : ''}`;
 | 
					export const serverurl = `${window.location.protocol}//${domain || window.location.hostname}${port ? ':' + port : ''}${urlpath ? '/' + urlpath : ''}`
 | 
				
			||||||
window.serverurl = serverurl;
 | 
					window.serverurl = serverurl
 | 
				
			||||||
export const noteid = urlpath ? window.location.pathname.slice(urlpath.length + 1, window.location.pathname.length).split('/')[1] : window.location.pathname.split('/')[1];
 | 
					export const noteid = urlpath ? window.location.pathname.slice(urlpath.length + 1, window.location.pathname.length).split('/')[1] : window.location.pathname.split('/')[1]
 | 
				
			||||||
export const noteurl = `${serverurl}/${noteid}`;
 | 
					export const noteurl = `${serverurl}/${noteid}`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const version = '0.5.0';
 | 
					export const version = '0.5.0'
 | 
				
			||||||
 | 
				
			|||||||
@ -1,26 +1,28 @@
 | 
				
			|||||||
var lang = "en";
 | 
					/* eslint-env browser, jquery */
 | 
				
			||||||
var userLang = navigator.language || navigator.userLanguage;
 | 
					/* global Cookies */
 | 
				
			||||||
var userLangCode = userLang.split('-')[0];
 | 
					
 | 
				
			||||||
var userCountryCode = userLang.split('-')[1];
 | 
					var lang = 'en'
 | 
				
			||||||
var locale = $('.ui-locale');
 | 
					var userLang = navigator.language || navigator.userLanguage
 | 
				
			||||||
var supportLangs = [];
 | 
					var userLangCode = userLang.split('-')[0]
 | 
				
			||||||
$(".ui-locale option").each(function() {
 | 
					var locale = $('.ui-locale')
 | 
				
			||||||
    supportLangs.push($(this).val());
 | 
					var supportLangs = []
 | 
				
			||||||
});
 | 
					$('.ui-locale option').each(function () {
 | 
				
			||||||
 | 
					  supportLangs.push($(this).val())
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
if (Cookies.get('locale')) {
 | 
					if (Cookies.get('locale')) {
 | 
				
			||||||
    lang = Cookies.get('locale');
 | 
					  lang = Cookies.get('locale')
 | 
				
			||||||
} else if (supportLangs.indexOf(userLang) !== -1) {
 | 
					} else if (supportLangs.indexOf(userLang) !== -1) {
 | 
				
			||||||
    lang = supportLangs[supportLangs.indexOf(userLang)];
 | 
					  lang = supportLangs[supportLangs.indexOf(userLang)]
 | 
				
			||||||
} else if (supportLangs.indexOf(userLangCode) !== -1) {
 | 
					} else if (supportLangs.indexOf(userLangCode) !== -1) {
 | 
				
			||||||
    lang = supportLangs[supportLangs.indexOf(userLangCode)];
 | 
					  lang = supportLangs[supportLangs.indexOf(userLangCode)]
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
locale.val(lang);
 | 
					locale.val(lang)
 | 
				
			||||||
$('select.ui-locale option[value="' + lang + '"]').attr('selected','selected');
 | 
					$('select.ui-locale option[value="' + lang + '"]').attr('selected', 'selected')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
locale.change(function() {
 | 
					locale.change(function () {
 | 
				
			||||||
  Cookies.set('locale', $(this).val(), {
 | 
					  Cookies.set('locale', $(this).val(), {
 | 
				
			||||||
    expires: 365
 | 
					    expires: 365
 | 
				
			||||||
    });
 | 
					  })
 | 
				
			||||||
    window.location.reload();
 | 
					  window.location.reload()
 | 
				
			||||||
});
 | 
					})
 | 
				
			||||||
 | 
				
			|||||||
@ -1,8 +1,11 @@
 | 
				
			|||||||
require('../css/extra.css');
 | 
					/* eslint-env browser, jquery */
 | 
				
			||||||
require('../css/slide-preview.css');
 | 
					/* global refreshView */
 | 
				
			||||||
require('../css/site.css');
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
require('highlight.js/styles/github-gist.css');
 | 
					require('../css/extra.css')
 | 
				
			||||||
 | 
					require('../css/slide-preview.css')
 | 
				
			||||||
 | 
					require('../css/site.css')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					require('highlight.js/styles/github-gist.css')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
    autoLinkify,
 | 
					    autoLinkify,
 | 
				
			||||||
@ -16,126 +19,126 @@ import {
 | 
				
			|||||||
    scrollToHash,
 | 
					    scrollToHash,
 | 
				
			||||||
    smoothHashScroll,
 | 
					    smoothHashScroll,
 | 
				
			||||||
    updateLastChange
 | 
					    updateLastChange
 | 
				
			||||||
} from './extra';
 | 
					} from './extra'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { preventXSS } from './render';
 | 
					import { preventXSS } from './render'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const markdown = $("#doc.markdown-body");
 | 
					const markdown = $('#doc.markdown-body')
 | 
				
			||||||
const text = markdown.text();
 | 
					const text = markdown.text()
 | 
				
			||||||
const lastMeta = md.meta;
 | 
					const lastMeta = md.meta
 | 
				
			||||||
md.meta = {};
 | 
					md.meta = {}
 | 
				
			||||||
delete md.metaError;
 | 
					delete md.metaError
 | 
				
			||||||
let rendered = md.render(text);
 | 
					let rendered = md.render(text)
 | 
				
			||||||
if (md.meta.type && md.meta.type === 'slide') {
 | 
					if (md.meta.type && md.meta.type === 'slide') {
 | 
				
			||||||
  const slideOptions = {
 | 
					  const slideOptions = {
 | 
				
			||||||
    separator: '^(\r\n?|\n)---(\r\n?|\n)$',
 | 
					    separator: '^(\r\n?|\n)---(\r\n?|\n)$',
 | 
				
			||||||
    verticalSeparator: '^(\r\n?|\n)----(\r\n?|\n)$'
 | 
					    verticalSeparator: '^(\r\n?|\n)----(\r\n?|\n)$'
 | 
				
			||||||
    };
 | 
					  }
 | 
				
			||||||
    const slides = RevealMarkdown.slidify(text, slideOptions);
 | 
					  const slides = window.RevealMarkdown.slidify(text, slideOptions)
 | 
				
			||||||
    markdown.html(slides);
 | 
					  markdown.html(slides)
 | 
				
			||||||
    RevealMarkdown.initialize();
 | 
					  window.RevealMarkdown.initialize()
 | 
				
			||||||
    // prevent XSS
 | 
					    // prevent XSS
 | 
				
			||||||
    markdown.html(preventXSS(markdown.html()));
 | 
					  markdown.html(preventXSS(markdown.html()))
 | 
				
			||||||
    markdown.addClass('slides');
 | 
					  markdown.addClass('slides')
 | 
				
			||||||
} else {
 | 
					} else {
 | 
				
			||||||
  if (lastMeta.type && lastMeta.type === 'slide') {
 | 
					  if (lastMeta.type && lastMeta.type === 'slide') {
 | 
				
			||||||
        refreshView();
 | 
					    refreshView()
 | 
				
			||||||
        markdown.removeClass('slides');
 | 
					    markdown.removeClass('slides')
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
    // only render again when meta changed
 | 
					    // only render again when meta changed
 | 
				
			||||||
    if (JSON.stringify(md.meta) != JSON.stringify(lastMeta)) {
 | 
					  if (JSON.stringify(md.meta) !== JSON.stringify(lastMeta)) {
 | 
				
			||||||
        parseMeta(md, null, markdown, $('#ui-toc'), $('#ui-toc-affix'));
 | 
					    parseMeta(md, null, markdown, $('#ui-toc'), $('#ui-toc-affix'))
 | 
				
			||||||
        rendered = md.render(text);
 | 
					    rendered = md.render(text)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
    // prevent XSS
 | 
					    // prevent XSS
 | 
				
			||||||
    rendered = preventXSS(rendered);
 | 
					  rendered = preventXSS(rendered)
 | 
				
			||||||
    const result = postProcess(rendered);
 | 
					  const result = postProcess(rendered)
 | 
				
			||||||
    markdown.html(result.html());
 | 
					  markdown.html(result.html())
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
$(document.body).show();
 | 
					$(document.body).show()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
finishView(markdown);
 | 
					finishView(markdown)
 | 
				
			||||||
autoLinkify(markdown);
 | 
					autoLinkify(markdown)
 | 
				
			||||||
deduplicatedHeaderId(markdown);
 | 
					deduplicatedHeaderId(markdown)
 | 
				
			||||||
renderTOC(markdown);
 | 
					renderTOC(markdown)
 | 
				
			||||||
generateToc('ui-toc');
 | 
					generateToc('ui-toc')
 | 
				
			||||||
generateToc('ui-toc-affix');
 | 
					generateToc('ui-toc-affix')
 | 
				
			||||||
smoothHashScroll();
 | 
					smoothHashScroll()
 | 
				
			||||||
createtime = lastchangeui.time.attr('data-createtime');
 | 
					window.createtime = window.lastchangeui.time.attr('data-createtime')
 | 
				
			||||||
lastchangetime = lastchangeui.time.attr('data-updatetime');
 | 
					window.lastchangetime = window.lastchangeui.time.attr('data-updatetime')
 | 
				
			||||||
updateLastChange();
 | 
					updateLastChange()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const url = window.location.pathname;
 | 
					const url = window.location.pathname
 | 
				
			||||||
$('.ui-edit').attr('href', `${url}/edit`);
 | 
					$('.ui-edit').attr('href', `${url}/edit`)
 | 
				
			||||||
const toc = $('.ui-toc');
 | 
					const toc = $('.ui-toc')
 | 
				
			||||||
const tocAffix = $('.ui-affix-toc');
 | 
					const tocAffix = $('.ui-affix-toc')
 | 
				
			||||||
const tocDropdown = $('.ui-toc-dropdown');
 | 
					const tocDropdown = $('.ui-toc-dropdown')
 | 
				
			||||||
//toc
 | 
					// toc
 | 
				
			||||||
tocDropdown.click(e => {
 | 
					tocDropdown.click(e => {
 | 
				
			||||||
    e.stopPropagation();
 | 
					  e.stopPropagation()
 | 
				
			||||||
});
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
let enoughForAffixToc = true;
 | 
					let enoughForAffixToc = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function generateScrollspy() {
 | 
					function generateScrollspy () {
 | 
				
			||||||
  $(document.body).scrollspy({
 | 
					  $(document.body).scrollspy({
 | 
				
			||||||
    target: ''
 | 
					    target: ''
 | 
				
			||||||
    });
 | 
					  })
 | 
				
			||||||
    $(document.body).scrollspy('refresh');
 | 
					  $(document.body).scrollspy('refresh')
 | 
				
			||||||
  if (enoughForAffixToc) {
 | 
					  if (enoughForAffixToc) {
 | 
				
			||||||
        toc.hide();
 | 
					    toc.hide()
 | 
				
			||||||
        tocAffix.show();
 | 
					    tocAffix.show()
 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
        tocAffix.hide();
 | 
					    tocAffix.hide()
 | 
				
			||||||
        toc.show();
 | 
					    toc.show()
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
    $(document.body).scroll();
 | 
					  $(document.body).scroll()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function windowResize() {
 | 
					function windowResize () {
 | 
				
			||||||
    //toc right
 | 
					    // toc right
 | 
				
			||||||
    const paddingRight = parseFloat(markdown.css('padding-right'));
 | 
					  const paddingRight = parseFloat(markdown.css('padding-right'))
 | 
				
			||||||
    const right = ($(window).width() - (markdown.offset().left + markdown.outerWidth() - paddingRight));
 | 
					  const right = ($(window).width() - (markdown.offset().left + markdown.outerWidth() - paddingRight))
 | 
				
			||||||
    toc.css('right', `${right}px`);
 | 
					  toc.css('right', `${right}px`)
 | 
				
			||||||
    //affix toc left
 | 
					    // affix toc left
 | 
				
			||||||
    let newbool;
 | 
					  let newbool
 | 
				
			||||||
    const rightMargin = (markdown.parent().outerWidth() - markdown.outerWidth()) / 2;
 | 
					  const rightMargin = (markdown.parent().outerWidth() - markdown.outerWidth()) / 2
 | 
				
			||||||
    //for ipad or wider device
 | 
					    // for ipad or wider device
 | 
				
			||||||
  if (rightMargin >= 133) {
 | 
					  if (rightMargin >= 133) {
 | 
				
			||||||
        newbool = true;
 | 
					    newbool = true
 | 
				
			||||||
        const affixLeftMargin = (tocAffix.outerWidth() - tocAffix.width()) / 2;
 | 
					    const affixLeftMargin = (tocAffix.outerWidth() - tocAffix.width()) / 2
 | 
				
			||||||
        const left = markdown.offset().left + markdown.outerWidth() - affixLeftMargin;
 | 
					    const left = markdown.offset().left + markdown.outerWidth() - affixLeftMargin
 | 
				
			||||||
        tocAffix.css('left', `${left}px`);
 | 
					    tocAffix.css('left', `${left}px`)
 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
        newbool = false;
 | 
					    newbool = false
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
    if (newbool != enoughForAffixToc) {
 | 
					  if (newbool !== enoughForAffixToc) {
 | 
				
			||||||
        enoughForAffixToc = newbool;
 | 
					    enoughForAffixToc = newbool
 | 
				
			||||||
        generateScrollspy();
 | 
					    generateScrollspy()
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
$(window).resize(() => {
 | 
					$(window).resize(() => {
 | 
				
			||||||
    windowResize();
 | 
					  windowResize()
 | 
				
			||||||
});
 | 
					})
 | 
				
			||||||
$(document).ready(() => {
 | 
					$(document).ready(() => {
 | 
				
			||||||
    windowResize();
 | 
					  windowResize()
 | 
				
			||||||
    generateScrollspy();
 | 
					  generateScrollspy()
 | 
				
			||||||
    setTimeout(scrollToHash, 0);
 | 
					  setTimeout(scrollToHash, 0)
 | 
				
			||||||
    //tooltip
 | 
					    // tooltip
 | 
				
			||||||
    $('[data-toggle="tooltip"]').tooltip();
 | 
					  $('[data-toggle="tooltip"]').tooltip()
 | 
				
			||||||
});
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function scrollToTop() {
 | 
					export function scrollToTop () {
 | 
				
			||||||
  $('body, html').stop(true, true).animate({
 | 
					  $('body, html').stop(true, true).animate({
 | 
				
			||||||
    scrollTop: 0
 | 
					    scrollTop: 0
 | 
				
			||||||
    }, 100, "linear");
 | 
					  }, 100, 'linear')
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function scrollToBottom() {
 | 
					export function scrollToBottom () {
 | 
				
			||||||
  $('body, html').stop(true, true).animate({
 | 
					  $('body, html').stop(true, true).animate({
 | 
				
			||||||
    scrollTop: $(document.body)[0].scrollHeight
 | 
					    scrollTop: $(document.body)[0].scrollHeight
 | 
				
			||||||
    }, 100, "linear");
 | 
					  }, 100, 'linear')
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
window.scrollToTop = scrollToTop;
 | 
					window.scrollToTop = scrollToTop
 | 
				
			||||||
window.scrollToBottom = scrollToBottom;
 | 
					window.scrollToBottom = scrollToBottom
 | 
				
			||||||
 | 
				
			|||||||
@ -1,62 +1,64 @@
 | 
				
			|||||||
 | 
					/* eslint-env browser, jquery */
 | 
				
			||||||
 | 
					/* global filterXSS */
 | 
				
			||||||
// allow some attributes
 | 
					// allow some attributes
 | 
				
			||||||
var whiteListAttr = ['id', 'class', 'style'];
 | 
					var whiteListAttr = ['id', 'class', 'style']
 | 
				
			||||||
window.whiteListAttr = whiteListAttr;
 | 
					window.whiteListAttr = whiteListAttr
 | 
				
			||||||
// allow link starts with '.', '/' and custom protocol with '://'
 | 
					// allow link starts with '.', '/' and custom protocol with '://'
 | 
				
			||||||
var linkRegex = /^([\w|-]+:\/\/)|^([\.|\/])+/;
 | 
					var linkRegex = /^([\w|-]+:\/\/)|^([.|/])+/
 | 
				
			||||||
// allow data uri, from https://gist.github.com/bgrins/6194623
 | 
					// allow data uri, from https://gist.github.com/bgrins/6194623
 | 
				
			||||||
var dataUriRegex = /^\s*data:([a-z]+\/[a-z0-9-+.]+(;[a-z-]+=[a-z0-9-]+)?)?(;base64)?,([a-z0-9!$&',()*+;=\-._~:@\/?%\s]*)\s*$/i;
 | 
					var dataUriRegex = /^\s*data:([a-z]+\/[a-z0-9-+.]+(;[a-z-]+=[a-z0-9-]+)?)?(;base64)?,([a-z0-9!$&',()*+;=\-._~:@/?%\s]*)\s*$/i
 | 
				
			||||||
// custom white list
 | 
					// custom white list
 | 
				
			||||||
var whiteList = filterXSS.whiteList;
 | 
					var whiteList = filterXSS.whiteList
 | 
				
			||||||
// allow ol specify start number
 | 
					// allow ol specify start number
 | 
				
			||||||
whiteList['ol'] = ['start'];
 | 
					whiteList['ol'] = ['start']
 | 
				
			||||||
// allow li specify value number
 | 
					// allow li specify value number
 | 
				
			||||||
whiteList['li'] = ['value'];
 | 
					whiteList['li'] = ['value']
 | 
				
			||||||
// allow style tag
 | 
					// allow style tag
 | 
				
			||||||
whiteList['style'] = [];
 | 
					whiteList['style'] = []
 | 
				
			||||||
// allow kbd tag
 | 
					// allow kbd tag
 | 
				
			||||||
whiteList['kbd'] = [];
 | 
					whiteList['kbd'] = []
 | 
				
			||||||
// allow ifram tag with some safe attributes
 | 
					// allow ifram tag with some safe attributes
 | 
				
			||||||
whiteList['iframe'] = ['allowfullscreen', 'name', 'referrerpolicy', 'sandbox', 'src', 'srcdoc', 'width', 'height'];
 | 
					whiteList['iframe'] = ['allowfullscreen', 'name', 'referrerpolicy', 'sandbox', 'src', 'srcdoc', 'width', 'height']
 | 
				
			||||||
// allow summary tag
 | 
					// allow summary tag
 | 
				
			||||||
whiteList['summary'] = [];
 | 
					whiteList['summary'] = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var filterXSSOptions = {
 | 
					var filterXSSOptions = {
 | 
				
			||||||
  allowCommentTag: true,
 | 
					  allowCommentTag: true,
 | 
				
			||||||
  whiteList: whiteList,
 | 
					  whiteList: whiteList,
 | 
				
			||||||
  escapeHtml: function (html) {
 | 
					  escapeHtml: function (html) {
 | 
				
			||||||
        // allow html comment in multiple lines
 | 
					        // allow html comment in multiple lines
 | 
				
			||||||
        return html.replace(/<(.*?)>/g, '<$1>');
 | 
					    return html.replace(/<(.*?)>/g, '<$1>')
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  onIgnoreTag: function (tag, html, options) {
 | 
					  onIgnoreTag: function (tag, html, options) {
 | 
				
			||||||
        // allow comment tag
 | 
					        // allow comment tag
 | 
				
			||||||
        if (tag == "!--") {
 | 
					    if (tag === '!--') {
 | 
				
			||||||
            // do not filter its attributes
 | 
					            // do not filter its attributes
 | 
				
			||||||
            return html;
 | 
					      return html
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  onTagAttr: function (tag, name, value, isWhiteAttr) {
 | 
					  onTagAttr: function (tag, name, value, isWhiteAttr) {
 | 
				
			||||||
        // allow href and src that match linkRegex
 | 
					        // allow href and src that match linkRegex
 | 
				
			||||||
    if (isWhiteAttr && (name === 'href' || name === 'src') && linkRegex.test(value)) {
 | 
					    if (isWhiteAttr && (name === 'href' || name === 'src') && linkRegex.test(value)) {
 | 
				
			||||||
            return name + '="' + filterXSS.escapeAttrValue(value) + '"';
 | 
					      return name + '="' + filterXSS.escapeAttrValue(value) + '"'
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
        // allow data uri in img src
 | 
					        // allow data uri in img src
 | 
				
			||||||
        if (isWhiteAttr && (tag == "img" && name === 'src') && dataUriRegex.test(value)) {
 | 
					    if (isWhiteAttr && (tag === 'img' && name === 'src') && dataUriRegex.test(value)) {
 | 
				
			||||||
            return name + '="' + filterXSS.escapeAttrValue(value) + '"';
 | 
					      return name + '="' + filterXSS.escapeAttrValue(value) + '"'
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  onIgnoreTagAttr: function (tag, name, value, isWhiteAttr) {
 | 
					  onIgnoreTagAttr: function (tag, name, value, isWhiteAttr) {
 | 
				
			||||||
        // allow attr start with 'data-' or in the whiteListAttr
 | 
					        // allow attr start with 'data-' or in the whiteListAttr
 | 
				
			||||||
        if (name.substr(0, 5) === 'data-' || whiteListAttr.indexOf(name) !== -1) {
 | 
					    if (name.substr(0, 5) === 'data-' || window.whiteListAttr.indexOf(name) !== -1) {
 | 
				
			||||||
            // escape its value using built-in escapeAttrValue function
 | 
					            // escape its value using built-in escapeAttrValue function
 | 
				
			||||||
            return name + '="' + filterXSS.escapeAttrValue(value) + '"';
 | 
					      return name + '="' + filterXSS.escapeAttrValue(value) + '"'
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function preventXSS(html) {
 | 
					 | 
				
			||||||
    return filterXSS(html, filterXSSOptions);
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
window.preventXSS = preventXSS;
 | 
					
 | 
				
			||||||
 | 
					function preventXSS (html) {
 | 
				
			||||||
 | 
					  return filterXSS(html, filterXSSOptions)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					window.preventXSS = preventXSS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
module.exports = {
 | 
					module.exports = {
 | 
				
			||||||
  preventXSS: preventXSS
 | 
					  preventXSS: preventXSS
 | 
				
			||||||
 | 
				
			|||||||
@ -1,53 +1,52 @@
 | 
				
			|||||||
 | 
					/* eslint-env browser, jquery */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { preventXSS } from './render'
 | 
				
			||||||
 | 
					import { md } from './extra'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * The reveal.js markdown plugin. Handles parsing of
 | 
					 * The reveal.js markdown plugin. Handles parsing of
 | 
				
			||||||
 * markdown inside of presentations as well as loading
 | 
					 * markdown inside of presentations as well as loading
 | 
				
			||||||
 * of external markdown documents.
 | 
					 * of external markdown documents.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
(function( root, factory ) {
 | 
					(function (root, factory) {
 | 
				
			||||||
	if( typeof exports === 'object' ) {
 | 
					  if (typeof exports === 'object') {
 | 
				
			||||||
		module.exports = factory();
 | 
					    module.exports = factory()
 | 
				
			||||||
	}
 | 
					  } else {
 | 
				
			||||||
	else {
 | 
					 | 
				
			||||||
    // Browser globals (root is window)
 | 
					    // Browser globals (root is window)
 | 
				
			||||||
		root.RevealMarkdown = factory();
 | 
					    root.RevealMarkdown = factory()
 | 
				
			||||||
		root.RevealMarkdown.initialize();
 | 
					    root.RevealMarkdown.initialize()
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}( this, function() {
 | 
					}(this, function () {
 | 
				
			||||||
	
 | 
					  var DEFAULT_SLIDE_SEPARATOR = '^\r?\n---\r?\n$'
 | 
				
			||||||
	var DEFAULT_SLIDE_SEPARATOR = '^\r?\n---\r?\n$',
 | 
					  var DEFAULT_NOTES_SEPARATOR = 'note:'
 | 
				
			||||||
		DEFAULT_NOTES_SEPARATOR = 'note:',
 | 
					  var DEFAULT_ELEMENT_ATTRIBUTES_SEPARATOR = '\\.element\\s*?(.+?)$'
 | 
				
			||||||
		DEFAULT_ELEMENT_ATTRIBUTES_SEPARATOR = '\\\.element\\\s*?(.+?)$',
 | 
					  var DEFAULT_SLIDE_ATTRIBUTES_SEPARATOR = '\\.slide:\\s*?(\\S.+?)$'
 | 
				
			||||||
		DEFAULT_SLIDE_ATTRIBUTES_SEPARATOR = '\\\.slide:\\\s*?(\\\S.+?)$';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	var SCRIPT_END_PLACEHOLDER = '__SCRIPT_END__';
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  var SCRIPT_END_PLACEHOLDER = '__SCRIPT_END__'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
   * Retrieves the markdown contents of a slide section
 | 
					   * Retrieves the markdown contents of a slide section
 | 
				
			||||||
   * element. Normalizes leading tabs/whitespace.
 | 
					   * element. Normalizes leading tabs/whitespace.
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
	function getMarkdownFromSlide( section ) {
 | 
					  function getMarkdownFromSlide (section) {
 | 
				
			||||||
 | 
					    var template = section.querySelector('script')
 | 
				
			||||||
		var template = section.querySelector( 'script' );
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // strip leading whitespace so it isn't evaluated as code
 | 
					    // strip leading whitespace so it isn't evaluated as code
 | 
				
			||||||
		var text = ( template || section ).textContent;
 | 
					    var text = (template || section).textContent
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // restore script end tags
 | 
					    // restore script end tags
 | 
				
			||||||
		text = text.replace( new RegExp( SCRIPT_END_PLACEHOLDER, 'g' ), '</script>' );
 | 
					    text = text.replace(new RegExp(SCRIPT_END_PLACEHOLDER, 'g'), '</script>')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		var leadingWs = text.match( /^\n?(\s*)/ )[1].length,
 | 
					    var leadingWs = text.match(/^\n?(\s*)/)[1].length
 | 
				
			||||||
			leadingTabs = text.match( /^\n?(\t*)/ )[1].length;
 | 
					    var leadingTabs = text.match(/^\n?(\t*)/)[1].length
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if( leadingTabs > 0 ) {
 | 
					    if (leadingTabs > 0) {
 | 
				
			||||||
			text = text.replace( new RegExp('\\n?\\t{' + leadingTabs + '}','g'), '\n' );
 | 
					      text = text.replace(new RegExp('\\n?\\t{' + leadingTabs + '}', 'g'), '\n')
 | 
				
			||||||
		}
 | 
					    } else if (leadingWs > 1) {
 | 
				
			||||||
		else if( leadingWs > 1 ) {
 | 
					      text = text.replace(new RegExp('\\n? {' + leadingWs + '}', 'g'), '\n')
 | 
				
			||||||
			text = text.replace( new RegExp('\\n? {' + leadingWs + '}', 'g'), '\n' );
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return text;
 | 
					    return text
 | 
				
			||||||
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
@ -56,136 +55,123 @@
 | 
				
			|||||||
   * parsing. Used to forward any other user-defined arguments
 | 
					   * parsing. Used to forward any other user-defined arguments
 | 
				
			||||||
   * to the output markdown slide.
 | 
					   * to the output markdown slide.
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
	function getForwardedAttributes( section ) {
 | 
					  function getForwardedAttributes (section) {
 | 
				
			||||||
 | 
					    var attributes = section.attributes
 | 
				
			||||||
 | 
					    var result = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		var attributes = section.attributes;
 | 
					    for (var i = 0, len = attributes.length; i < len; i++) {
 | 
				
			||||||
		var result = [];
 | 
					      var name = attributes[i].name
 | 
				
			||||||
 | 
					      var value = attributes[i].value
 | 
				
			||||||
		for( var i = 0, len = attributes.length; i < len; i++ ) {
 | 
					 | 
				
			||||||
			var name = attributes[i].name,
 | 
					 | 
				
			||||||
				value = attributes[i].value;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
      // disregard attributes that are used for markdown loading/parsing
 | 
					      // disregard attributes that are used for markdown loading/parsing
 | 
				
			||||||
			if( /data\-(markdown|separator|vertical|notes)/gi.test( name ) ) continue;
 | 
					      if (/data-(markdown|separator|vertical|notes)/gi.test(name)) continue
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if( value ) {
 | 
					      if (value) {
 | 
				
			||||||
				result.push( name + '="' + value + '"' );
 | 
					        result.push(name + '="' + value + '"')
 | 
				
			||||||
			}
 | 
					      } else {
 | 
				
			||||||
			else {
 | 
					        result.push(name)
 | 
				
			||||||
				result.push( name );
 | 
					 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return result.join( ' ' );
 | 
					    return result.join(' ')
 | 
				
			||||||
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
   * Inspects the given options and fills out default
 | 
					   * Inspects the given options and fills out default
 | 
				
			||||||
   * values for what's not defined.
 | 
					   * values for what's not defined.
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
	function getSlidifyOptions( options ) {
 | 
					  function getSlidifyOptions (options) {
 | 
				
			||||||
 | 
					    options = options || {}
 | 
				
			||||||
		options = options || {};
 | 
					    options.separator = options.separator || DEFAULT_SLIDE_SEPARATOR
 | 
				
			||||||
		options.separator = options.separator || DEFAULT_SLIDE_SEPARATOR;
 | 
					    options.notesSeparator = options.notesSeparator || DEFAULT_NOTES_SEPARATOR
 | 
				
			||||||
		options.notesSeparator = options.notesSeparator || DEFAULT_NOTES_SEPARATOR;
 | 
					    options.attributes = options.attributes || ''
 | 
				
			||||||
		options.attributes = options.attributes || '';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		return options;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return options
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
   * Helper function for constructing a markdown slide.
 | 
					   * Helper function for constructing a markdown slide.
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
	function createMarkdownSlide( content, options ) {
 | 
					  function createMarkdownSlide (content, options) {
 | 
				
			||||||
 | 
					    options = getSlidifyOptions(options)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		options = getSlidifyOptions( options );
 | 
					    var notesMatch = content.split(new RegExp(options.notesSeparator, 'mgi'))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		var notesMatch = content.split( new RegExp( options.notesSeparator, 'mgi' ) );
 | 
					    if (notesMatch.length === 2) {
 | 
				
			||||||
 | 
					      content = notesMatch[0] + '<aside class="notes" data-markdown>' + notesMatch[1].trim() + '</aside>'
 | 
				
			||||||
		if( notesMatch.length === 2 ) {
 | 
					 | 
				
			||||||
			content = notesMatch[0] + '<aside class="notes" data-markdown>' + notesMatch[1].trim() + '</aside>';
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // prevent script end tags in the content from interfering
 | 
					    // prevent script end tags in the content from interfering
 | 
				
			||||||
    // with parsing
 | 
					    // with parsing
 | 
				
			||||||
		content = content.replace( /<\/script>/g, SCRIPT_END_PLACEHOLDER );
 | 
					    content = content.replace(/<\/script>/g, SCRIPT_END_PLACEHOLDER)
 | 
				
			||||||
 | 
					 | 
				
			||||||
		return '<script type="text/template">' + content + '</script>';
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return '<script type="text/template">' + content + '</script>'
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
   * Parses a data string into multiple slides based
 | 
					   * Parses a data string into multiple slides based
 | 
				
			||||||
   * on the passed in separator arguments.
 | 
					   * on the passed in separator arguments.
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
	function slidify( markdown, options ) {
 | 
					  function slidify (markdown, options) {
 | 
				
			||||||
 | 
					    options = getSlidifyOptions(options)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		options = getSlidifyOptions( options );
 | 
					    var separatorRegex = new RegExp(options.separator + (options.verticalSeparator ? '|' + options.verticalSeparator : ''), 'mg')
 | 
				
			||||||
 | 
					    var horizontalSeparatorRegex = new RegExp(options.separator)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		var separatorRegex = new RegExp( options.separator + ( options.verticalSeparator ? '|' + options.verticalSeparator : '' ), 'mg' ),
 | 
					    var matches
 | 
				
			||||||
			horizontalSeparatorRegex = new RegExp( options.separator );
 | 
					    var lastIndex = 0
 | 
				
			||||||
 | 
					    var isHorizontal
 | 
				
			||||||
		var matches,
 | 
					    var wasHorizontal = true
 | 
				
			||||||
			lastIndex = 0,
 | 
					    var content
 | 
				
			||||||
			isHorizontal,
 | 
					    var sectionStack = []
 | 
				
			||||||
			wasHorizontal = true,
 | 
					 | 
				
			||||||
			content,
 | 
					 | 
				
			||||||
			sectionStack = [];
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // iterate until all blocks between separators are stacked up
 | 
					    // iterate until all blocks between separators are stacked up
 | 
				
			||||||
		while( matches = separatorRegex.exec( markdown ) ) {
 | 
					    while ((matches = separatorRegex.exec(markdown)) !== null) {
 | 
				
			||||||
			notes = null;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      // determine direction (horizontal by default)
 | 
					      // determine direction (horizontal by default)
 | 
				
			||||||
			isHorizontal = horizontalSeparatorRegex.test( matches[0] );
 | 
					      isHorizontal = horizontalSeparatorRegex.test(matches[0])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if( !isHorizontal && wasHorizontal ) {
 | 
					      if (!isHorizontal && wasHorizontal) {
 | 
				
			||||||
        // create vertical stack
 | 
					        // create vertical stack
 | 
				
			||||||
				sectionStack.push( [] );
 | 
					        sectionStack.push([])
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      // pluck slide content from markdown input
 | 
					      // pluck slide content from markdown input
 | 
				
			||||||
			content = markdown.substring( lastIndex, matches.index );
 | 
					      content = markdown.substring(lastIndex, matches.index)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if( isHorizontal && wasHorizontal ) {
 | 
					      if (isHorizontal && wasHorizontal) {
 | 
				
			||||||
        // add to horizontal stack
 | 
					        // add to horizontal stack
 | 
				
			||||||
				sectionStack.push( content );
 | 
					        sectionStack.push(content)
 | 
				
			||||||
			}
 | 
					      } else {
 | 
				
			||||||
			else {
 | 
					 | 
				
			||||||
        // add to vertical stack
 | 
					        // add to vertical stack
 | 
				
			||||||
				sectionStack[sectionStack.length-1].push( content );
 | 
					        sectionStack[sectionStack.length - 1].push(content)
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			lastIndex = separatorRegex.lastIndex;
 | 
					      lastIndex = separatorRegex.lastIndex
 | 
				
			||||||
			wasHorizontal = isHorizontal;
 | 
					      wasHorizontal = isHorizontal
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // add the remaining slide
 | 
					    // add the remaining slide
 | 
				
			||||||
		( wasHorizontal ? sectionStack : sectionStack[sectionStack.length-1] ).push( markdown.substring( lastIndex ) );
 | 
					    (wasHorizontal ? sectionStack : sectionStack[sectionStack.length - 1]).push(markdown.substring(lastIndex))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		var markdownSections = '';
 | 
					    var markdownSections = ''
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // flatten the hierarchical stack, and insert <section data-markdown> tags
 | 
					    // flatten the hierarchical stack, and insert <section data-markdown> tags
 | 
				
			||||||
		for( var i = 0, len = sectionStack.length; i < len; i++ ) {
 | 
					    for (var i = 0, len = sectionStack.length; i < len; i++) {
 | 
				
			||||||
      // vertical
 | 
					      // vertical
 | 
				
			||||||
			if( sectionStack[i] instanceof Array ) {
 | 
					      if (sectionStack[i] instanceof Array) {
 | 
				
			||||||
				markdownSections += '<section '+ options.attributes +'>';
 | 
					        markdownSections += '<section ' + options.attributes + '>'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				sectionStack[i].forEach( function( child ) {
 | 
					        sectionStack[i].forEach(function (child) {
 | 
				
			||||||
					markdownSections += '<section data-markdown>' +  createMarkdownSlide( child, options ) + '</section>';
 | 
					          markdownSections += '<section data-markdown>' + createMarkdownSlide(child, options) + '</section>'
 | 
				
			||||||
				} );
 | 
					        })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				markdownSections += '</section>';
 | 
					        markdownSections += '</section>'
 | 
				
			||||||
			}
 | 
					      } else {
 | 
				
			||||||
			else {
 | 
					        markdownSections += '<section ' + options.attributes + ' data-markdown>' + createMarkdownSlide(sectionStack[i], options) + '</section>'
 | 
				
			||||||
				markdownSections += '<section '+ options.attributes +' data-markdown>' + createMarkdownSlide( sectionStack[i], options ) + '</section>';
 | 
					 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return markdownSections;
 | 
					    return markdownSections
 | 
				
			||||||
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
@ -193,77 +179,62 @@
 | 
				
			|||||||
   * multi-slide markdown into separate sections and
 | 
					   * multi-slide markdown into separate sections and
 | 
				
			||||||
   * handles loading of external markdown.
 | 
					   * handles loading of external markdown.
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
	function processSlides() {
 | 
					  function processSlides () {
 | 
				
			||||||
 | 
					    var sections = document.querySelectorAll('[data-markdown]')
 | 
				
			||||||
 | 
					    var section
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		var sections = document.querySelectorAll( '[data-markdown]'),
 | 
					    for (var i = 0, len = sections.length; i < len; i++) {
 | 
				
			||||||
			section;
 | 
					      section = sections[i]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		for( var i = 0, len = sections.length; i < len; i++ ) {
 | 
					      if (section.getAttribute('data-markdown').length) {
 | 
				
			||||||
 | 
					        var xhr = new XMLHttpRequest()
 | 
				
			||||||
 | 
					        var url = section.getAttribute('data-markdown')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			section = sections[i];
 | 
					        var datacharset = section.getAttribute('data-charset')
 | 
				
			||||||
 | 
					 | 
				
			||||||
			if( section.getAttribute( 'data-markdown' ).length ) {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				var xhr = new XMLHttpRequest(),
 | 
					 | 
				
			||||||
					url = section.getAttribute( 'data-markdown' );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				datacharset = section.getAttribute( 'data-charset' );
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // see https://developer.mozilla.org/en-US/docs/Web/API/element.getAttribute#Notes
 | 
					        // see https://developer.mozilla.org/en-US/docs/Web/API/element.getAttribute#Notes
 | 
				
			||||||
				if( datacharset != null && datacharset != '' ) {
 | 
					        if (datacharset !== null && datacharset !== '') {
 | 
				
			||||||
					xhr.overrideMimeType( 'text/html; charset=' + datacharset );
 | 
					          xhr.overrideMimeType('text/html; charset=' + datacharset)
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				xhr.onreadystatechange = function() {
 | 
					        xhr.onreadystatechange = function () {
 | 
				
			||||||
					if( xhr.readyState === 4 ) {
 | 
					          if (xhr.readyState === 4) {
 | 
				
			||||||
            // file protocol yields status code 0 (useful for local debug, mobile applications etc.)
 | 
					            // file protocol yields status code 0 (useful for local debug, mobile applications etc.)
 | 
				
			||||||
						if ( ( xhr.status >= 200 && xhr.status < 300 ) || xhr.status === 0 ) {
 | 
					            if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 0) {
 | 
				
			||||||
 | 
					              section.outerHTML = slidify(xhr.responseText, {
 | 
				
			||||||
							section.outerHTML = slidify( xhr.responseText, {
 | 
					                separator: section.getAttribute('data-separator'),
 | 
				
			||||||
								separator: section.getAttribute( 'data-separator' ),
 | 
					                verticalSeparator: section.getAttribute('data-separator-vertical'),
 | 
				
			||||||
								verticalSeparator: section.getAttribute( 'data-separator-vertical' ),
 | 
					                notesSeparator: section.getAttribute('data-separator-notes'),
 | 
				
			||||||
								notesSeparator: section.getAttribute( 'data-separator-notes' ),
 | 
					                attributes: getForwardedAttributes(section)
 | 
				
			||||||
								attributes: getForwardedAttributes( section )
 | 
					              })
 | 
				
			||||||
							});
 | 
					            } else {
 | 
				
			||||||
 | 
					 | 
				
			||||||
						}
 | 
					 | 
				
			||||||
						else {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
              section.outerHTML = '<section data-state="alert">' +
 | 
					              section.outerHTML = '<section data-state="alert">' +
 | 
				
			||||||
              'ERROR: The attempt to fetch ' + url + ' failed with HTTP status ' + xhr.status + '.' +
 | 
					              'ERROR: The attempt to fetch ' + url + ' failed with HTTP status ' + xhr.status + '.' +
 | 
				
			||||||
              'Check your browser\'s JavaScript console for more details.' +
 | 
					              'Check your browser\'s JavaScript console for more details.' +
 | 
				
			||||||
              '<p>Remember that you need to serve the presentation HTML from a HTTP server.</p>' +
 | 
					              '<p>Remember that you need to serve the presentation HTML from a HTTP server.</p>' +
 | 
				
			||||||
								'</section>';
 | 
					              '</section>'
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
				};
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
				xhr.open( 'GET', url, false );
 | 
					        xhr.open('GET', url, false)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
					xhr.send();
 | 
					          xhr.send()
 | 
				
			||||||
 | 
					        } catch (e) {
 | 
				
			||||||
 | 
					          alert('Failed to get the Markdown file ' + url + '. Make sure that the presentation and the file are served by a HTTP server and the file can be found there. ' + e)
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
				catch ( e ) {
 | 
					      } else if (section.getAttribute('data-separator') || section.getAttribute('data-separator-vertical') || section.getAttribute('data-separator-notes')) {
 | 
				
			||||||
					alert( 'Failed to get the Markdown file ' + url + '. Make sure that the presentation and the file are served by a HTTP server and the file can be found there. ' + e );
 | 
					        section.outerHTML = slidify(getMarkdownFromSlide(section), {
 | 
				
			||||||
				}
 | 
					          separator: section.getAttribute('data-separator'),
 | 
				
			||||||
 | 
					          verticalSeparator: section.getAttribute('data-separator-vertical'),
 | 
				
			||||||
			}
 | 
					          notesSeparator: section.getAttribute('data-separator-notes'),
 | 
				
			||||||
			else if( section.getAttribute( 'data-separator' ) || section.getAttribute( 'data-separator-vertical' ) || section.getAttribute( 'data-separator-notes' ) ) {
 | 
					          attributes: getForwardedAttributes(section)
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
				section.outerHTML = slidify( getMarkdownFromSlide( section ), {
 | 
					      } else {
 | 
				
			||||||
					separator: section.getAttribute( 'data-separator' ),
 | 
					        section.innerHTML = createMarkdownSlide(getMarkdownFromSlide(section))
 | 
				
			||||||
					verticalSeparator: section.getAttribute( 'data-separator-vertical' ),
 | 
					 | 
				
			||||||
					notesSeparator: section.getAttribute( 'data-separator-notes' ),
 | 
					 | 
				
			||||||
					attributes: getForwardedAttributes( section )
 | 
					 | 
				
			||||||
				});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			else {
 | 
					 | 
				
			||||||
				section.innerHTML = createMarkdownSlide( getMarkdownFromSlide( section ) );
 | 
					 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
@ -275,62 +246,60 @@
 | 
				
			|||||||
   * directly on refresh (F5)
 | 
					   * directly on refresh (F5)
 | 
				
			||||||
   * http://stackoverflow.com/questions/5690269/disabling-chrome-cache-for-website-development/7000899#answer-11786277
 | 
					   * http://stackoverflow.com/questions/5690269/disabling-chrome-cache-for-website-development/7000899#answer-11786277
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
	function addAttributeInElement( node, elementTarget, separator ) {
 | 
					  function addAttributeInElement (node, elementTarget, separator) {
 | 
				
			||||||
 | 
					    var mardownClassesInElementsRegex = new RegExp(separator, 'mg')
 | 
				
			||||||
		var mardownClassesInElementsRegex = new RegExp( separator, 'mg' );
 | 
					    var mardownClassRegex = new RegExp('([^"= ]+?)="([^"=]+?)"', 'mg')
 | 
				
			||||||
		var mardownClassRegex = new RegExp( "([^\"= ]+?)=\"([^\"=]+?)\"", 'mg' );
 | 
					    var nodeValue = node.nodeValue
 | 
				
			||||||
		var nodeValue = node.nodeValue;
 | 
					    var matches
 | 
				
			||||||
		if( matches = mardownClassesInElementsRegex.exec( nodeValue ) ) {
 | 
					    var matchesClass
 | 
				
			||||||
 | 
					    if ((matches = mardownClassesInElementsRegex.exec(nodeValue))) {
 | 
				
			||||||
			var classes = matches[1];
 | 
					      var classes = matches[1]
 | 
				
			||||||
			nodeValue = nodeValue.substring( 0, matches.index ) + nodeValue.substring( mardownClassesInElementsRegex.lastIndex );
 | 
					      nodeValue = nodeValue.substring(0, matches.index) + nodeValue.substring(mardownClassesInElementsRegex.lastIndex)
 | 
				
			||||||
			node.nodeValue = nodeValue;
 | 
					      node.nodeValue = nodeValue
 | 
				
			||||||
			while( matchesClass = mardownClassRegex.exec( classes ) ) {
 | 
					      while ((matchesClass = mardownClassRegex.exec(classes))) {
 | 
				
			||||||
				var name = matchesClass[1];
 | 
					        var name = matchesClass[1]
 | 
				
			||||||
				var value = matchesClass[2];
 | 
					        var value = matchesClass[2]
 | 
				
			||||||
				if (name.substr(0, 5) === 'data-' || whiteListAttr.indexOf(name) !== -1)
 | 
					        if (name.substr(0, 5) === 'data-' || window.whiteListAttr.indexOf(name) !== -1) { elementTarget.setAttribute(name, window.filterXSS.escapeAttrValue(value)) }
 | 
				
			||||||
					elementTarget.setAttribute( name, filterXSS.escapeAttrValue(value) );
 | 
					 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
			return true;
 | 
					      return true
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
		return false;
 | 
					    return false
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
   * Add attributes to the parent element of a text node,
 | 
					   * Add attributes to the parent element of a text node,
 | 
				
			||||||
   * or the element of an attribute node.
 | 
					   * or the element of an attribute node.
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
	function addAttributes( section, element, previousElement, separatorElementAttributes, separatorSectionAttributes ) {
 | 
					  function addAttributes (section, element, previousElement, separatorElementAttributes, separatorSectionAttributes) {
 | 
				
			||||||
 | 
					    if (element != null && element.childNodes !== undefined && element.childNodes.length > 0) {
 | 
				
			||||||
		if ( element != null && element.childNodes != undefined && element.childNodes.length > 0 ) {
 | 
					      var previousParentElement = element
 | 
				
			||||||
			previousParentElement = element;
 | 
					      for (var i = 0; i < element.childNodes.length; i++) {
 | 
				
			||||||
			for( var i = 0; i < element.childNodes.length; i++ ) {
 | 
					        var childElement = element.childNodes[i]
 | 
				
			||||||
				childElement = element.childNodes[i];
 | 
					        if (i > 0) {
 | 
				
			||||||
				if ( i > 0 ) {
 | 
					          let j = i - 1
 | 
				
			||||||
					j = i - 1;
 | 
					          while (j >= 0) {
 | 
				
			||||||
					while ( j >= 0 ) {
 | 
					            var aPreviousChildElement = element.childNodes[j]
 | 
				
			||||||
						aPreviousChildElement = element.childNodes[j];
 | 
					            if (typeof aPreviousChildElement.setAttribute === 'function' && aPreviousChildElement.tagName !== 'BR') {
 | 
				
			||||||
						if ( typeof aPreviousChildElement.setAttribute == 'function' && aPreviousChildElement.tagName != "BR" ) {
 | 
					              previousParentElement = aPreviousChildElement
 | 
				
			||||||
							previousParentElement = aPreviousChildElement;
 | 
					              break
 | 
				
			||||||
							break;
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
						j = j - 1;
 | 
					            j = j - 1
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
				parentSection = section;
 | 
					        var parentSection = section
 | 
				
			||||||
				if( childElement.nodeName ==  "section" ) {
 | 
					        if (childElement.nodeName === 'section') {
 | 
				
			||||||
					parentSection = childElement ;
 | 
					          parentSection = childElement
 | 
				
			||||||
					previousParentElement = childElement ;
 | 
					          previousParentElement = childElement
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
				if ( typeof childElement.setAttribute == 'function' || childElement.nodeType == Node.COMMENT_NODE ) {
 | 
					        if (typeof childElement.setAttribute === 'function' || childElement.nodeType === Node.COMMENT_NODE) {
 | 
				
			||||||
					addAttributes( parentSection, childElement, previousParentElement, separatorElementAttributes, separatorSectionAttributes );
 | 
					          addAttributes(parentSection, childElement, previousParentElement, separatorElementAttributes, separatorSectionAttributes)
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if ( element.nodeType == Node.COMMENT_NODE ) {
 | 
					    if (element.nodeType === Node.COMMENT_NODE) {
 | 
				
			||||||
			if ( addAttributeInElement( element, previousElement, separatorElementAttributes ) == false ) {
 | 
					      if (addAttributeInElement(element, previousElement, separatorElementAttributes) === false) {
 | 
				
			||||||
				addAttributeInElement( element, section, separatorSectionAttributes );
 | 
					        addAttributeInElement(element, section, separatorSectionAttributes)
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@ -339,58 +308,48 @@
 | 
				
			|||||||
   * Converts any current data-markdown slides in the
 | 
					   * Converts any current data-markdown slides in the
 | 
				
			||||||
   * DOM to HTML.
 | 
					   * DOM to HTML.
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
	function convertSlides() {
 | 
					  function convertSlides () {
 | 
				
			||||||
 | 
					    var sections = document.querySelectorAll('[data-markdown]')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		var sections = document.querySelectorAll( '[data-markdown]');
 | 
					    for (var i = 0, len = sections.length; i < len; i++) {
 | 
				
			||||||
 | 
					      var section = sections[i]
 | 
				
			||||||
		for( var i = 0, len = sections.length; i < len; i++ ) {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			var section = sections[i];
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
      // Only parse the same slide once
 | 
					      // Only parse the same slide once
 | 
				
			||||||
			if( !section.getAttribute( 'data-markdown-parsed' ) ) {
 | 
					      if (!section.getAttribute('data-markdown-parsed')) {
 | 
				
			||||||
 | 
					        section.setAttribute('data-markdown-parsed', true)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				section.setAttribute( 'data-markdown-parsed', true )
 | 
					        var notes = section.querySelector('aside.notes')
 | 
				
			||||||
 | 
					        var markdown = getMarkdownFromSlide(section)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				var notes = section.querySelector( 'aside.notes' );
 | 
					        var rendered = md.render(markdown)
 | 
				
			||||||
				var markdown = getMarkdownFromSlide( section );
 | 
					        rendered = preventXSS(rendered)
 | 
				
			||||||
 | 
					        var result = window.postProcess(rendered)
 | 
				
			||||||
                var rendered = md.render(markdown);
 | 
					        section.innerHTML = result[0].outerHTML
 | 
				
			||||||
				rendered = preventXSS(rendered);
 | 
					        addAttributes(section, section, null, section.getAttribute('data-element-attributes') ||
 | 
				
			||||||
				var result = postProcess(rendered);
 | 
					        section.parentNode.getAttribute('data-element-attributes') ||
 | 
				
			||||||
				section.innerHTML = result[0].outerHTML;
 | 
					 | 
				
			||||||
				addAttributes( 	section, section, null, section.getAttribute( 'data-element-attributes' ) ||
 | 
					 | 
				
			||||||
								section.parentNode.getAttribute( 'data-element-attributes' ) ||
 | 
					 | 
				
			||||||
        DEFAULT_ELEMENT_ATTRIBUTES_SEPARATOR,
 | 
					        DEFAULT_ELEMENT_ATTRIBUTES_SEPARATOR,
 | 
				
			||||||
								section.getAttribute( 'data-attributes' ) ||
 | 
					        section.getAttribute('data-attributes') ||
 | 
				
			||||||
								section.parentNode.getAttribute( 'data-attributes' ) ||
 | 
					        section.parentNode.getAttribute('data-attributes') ||
 | 
				
			||||||
								DEFAULT_SLIDE_ATTRIBUTES_SEPARATOR);
 | 
					        DEFAULT_SLIDE_ATTRIBUTES_SEPARATOR)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // If there were notes, we need to re-add them after
 | 
					        // If there were notes, we need to re-add them after
 | 
				
			||||||
        // having overwritten the section's HTML
 | 
					        // having overwritten the section's HTML
 | 
				
			||||||
				if( notes ) {
 | 
					        if (notes) {
 | 
				
			||||||
					section.appendChild( notes );
 | 
					          section.appendChild(notes)
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // API
 | 
					  // API
 | 
				
			||||||
  return {
 | 
					  return {
 | 
				
			||||||
 | 
					    initialize: function () {
 | 
				
			||||||
		initialize: function() {
 | 
					      processSlides()
 | 
				
			||||||
			processSlides();
 | 
					      convertSlides()
 | 
				
			||||||
			convertSlides();
 | 
					 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					 | 
				
			||||||
    // TODO: Do these belong in the API?
 | 
					    // TODO: Do these belong in the API?
 | 
				
			||||||
    processSlides: processSlides,
 | 
					    processSlides: processSlides,
 | 
				
			||||||
    convertSlides: convertSlides,
 | 
					    convertSlides: convertSlides,
 | 
				
			||||||
    slidify: slidify
 | 
					    slidify: slidify
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
	};
 | 
					}))
 | 
				
			||||||
 | 
					 | 
				
			||||||
}));
 | 
					 | 
				
			||||||
 | 
				
			|||||||
@ -1,62 +1,63 @@
 | 
				
			|||||||
require('../css/extra.css');
 | 
					/* eslint-env browser, jquery */
 | 
				
			||||||
require('../css/site.css');
 | 
					/* global serverurl, Reveal */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { md, updateLastChange, finishView } from './extra';
 | 
					require('../css/extra.css')
 | 
				
			||||||
 | 
					require('../css/site.css')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { preventXSS } from './render';
 | 
					import { md, updateLastChange, finishView } from './extra'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const body = $(".slides").text();
 | 
					const body = $('.slides').text()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
createtime = lastchangeui.time.attr('data-createtime');
 | 
					window.createtime = window.lastchangeui.time.attr('data-createtime')
 | 
				
			||||||
lastchangetime = lastchangeui.time.attr('data-updatetime');
 | 
					window.lastchangetime = window.lastchangeui.time.attr('data-updatetime')
 | 
				
			||||||
updateLastChange();
 | 
					updateLastChange()
 | 
				
			||||||
const url = window.location.pathname;
 | 
					const url = window.location.pathname
 | 
				
			||||||
$('.ui-edit').attr('href', `${url}/edit`);
 | 
					$('.ui-edit').attr('href', `${url}/edit`)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
$(document).ready(() => {
 | 
					$(document).ready(() => {
 | 
				
			||||||
    //tooltip
 | 
					    // tooltip
 | 
				
			||||||
    $('[data-toggle="tooltip"]').tooltip();
 | 
					  $('[data-toggle="tooltip"]').tooltip()
 | 
				
			||||||
});
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function extend() {
 | 
					function extend () {
 | 
				
			||||||
    const target = {};
 | 
					  const target = {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  for (const source of arguments) {
 | 
					  for (const source of arguments) {
 | 
				
			||||||
    for (const key in source) {
 | 
					    for (const key in source) {
 | 
				
			||||||
      if (source.hasOwnProperty(key)) {
 | 
					      if (source.hasOwnProperty(key)) {
 | 
				
			||||||
                target[key] = source[key];
 | 
					        target[key] = source[key]
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return target;
 | 
					  return target
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Optional libraries used to extend on reveal.js
 | 
					// Optional libraries used to extend on reveal.js
 | 
				
			||||||
const deps = [{
 | 
					const deps = [{
 | 
				
			||||||
  src: `${serverurl}/build/reveal.js/lib/js/classList.js`,
 | 
					  src: `${serverurl}/build/reveal.js/lib/js/classList.js`,
 | 
				
			||||||
    condition() {
 | 
					  condition () {
 | 
				
			||||||
        return !document.body.classList;
 | 
					    return !document.body.classList
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}, {
 | 
					}, {
 | 
				
			||||||
  src: `${serverurl}/js/reveal-markdown.js`,
 | 
					  src: `${serverurl}/js/reveal-markdown.js`,
 | 
				
			||||||
    callback() {
 | 
					  callback () {
 | 
				
			||||||
    const slideOptions = {
 | 
					    const slideOptions = {
 | 
				
			||||||
      separator: '^(\r\n?|\n)---(\r\n?|\n)$',
 | 
					      separator: '^(\r\n?|\n)---(\r\n?|\n)$',
 | 
				
			||||||
      verticalSeparator: '^(\r\n?|\n)----(\r\n?|\n)$'
 | 
					      verticalSeparator: '^(\r\n?|\n)----(\r\n?|\n)$'
 | 
				
			||||||
        };
 | 
					    }
 | 
				
			||||||
        const slides = RevealMarkdown.slidify(body, slideOptions);
 | 
					    const slides = window.RevealMarkdown.slidify(body, slideOptions)
 | 
				
			||||||
        $(".slides").html(slides);
 | 
					    $('.slides').html(slides)
 | 
				
			||||||
        RevealMarkdown.initialize();
 | 
					    window.RevealMarkdown.initialize()
 | 
				
			||||||
        $(".slides").show();
 | 
					    $('.slides').show()
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}, {
 | 
					}, {
 | 
				
			||||||
  src: `${serverurl}/build/reveal.js/plugin/notes/notes.js`,
 | 
					  src: `${serverurl}/build/reveal.js/plugin/notes/notes.js`,
 | 
				
			||||||
  async: true,
 | 
					  async: true,
 | 
				
			||||||
    condition() {
 | 
					  condition () {
 | 
				
			||||||
        return !!document.body.classList;
 | 
					    return !!document.body.classList
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}];
 | 
					}]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// default options to init reveal.js
 | 
					// default options to init reveal.js
 | 
				
			||||||
const defaultOptions = {
 | 
					const defaultOptions = {
 | 
				
			||||||
@ -67,72 +68,72 @@ const defaultOptions = {
 | 
				
			|||||||
  center: true,
 | 
					  center: true,
 | 
				
			||||||
  transition: 'none',
 | 
					  transition: 'none',
 | 
				
			||||||
  dependencies: deps
 | 
					  dependencies: deps
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// options from yaml meta
 | 
					// options from yaml meta
 | 
				
			||||||
const meta = JSON.parse($("#meta").text());
 | 
					const meta = JSON.parse($('#meta').text())
 | 
				
			||||||
var options = meta.slideOptions || {};
 | 
					var options = meta.slideOptions || {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const view = $('.reveal');
 | 
					const view = $('.reveal')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//text language
 | 
					// text language
 | 
				
			||||||
if (meta.lang && typeof meta.lang == "string") {
 | 
					if (meta.lang && typeof meta.lang === 'string') {
 | 
				
			||||||
    view.attr('lang', meta.lang);
 | 
					  view.attr('lang', meta.lang)
 | 
				
			||||||
} else {
 | 
					} else {
 | 
				
			||||||
    view.removeAttr('lang');
 | 
					  view.removeAttr('lang')
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
//text direction
 | 
					// text direction
 | 
				
			||||||
if (meta.dir && typeof meta.dir == "string" && meta.dir == "rtl") {
 | 
					if (meta.dir && typeof meta.dir === 'string' && meta.dir === 'rtl') {
 | 
				
			||||||
    options.rtl = true;
 | 
					  options.rtl = true
 | 
				
			||||||
} else {
 | 
					} else {
 | 
				
			||||||
    options.rtl = false;
 | 
					  options.rtl = false
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
//breaks
 | 
					// breaks
 | 
				
			||||||
if (typeof meta.breaks === 'boolean' && !meta.breaks) {
 | 
					if (typeof meta.breaks === 'boolean' && !meta.breaks) {
 | 
				
			||||||
    md.options.breaks = false;
 | 
					  md.options.breaks = false
 | 
				
			||||||
} else {
 | 
					} else {
 | 
				
			||||||
    md.options.breaks = true;
 | 
					  md.options.breaks = true
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// options from URL query string
 | 
					// options from URL query string
 | 
				
			||||||
const queryOptions = Reveal.getQueryHash() || {};
 | 
					const queryOptions = Reveal.getQueryHash() || {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var options = extend(defaultOptions, options, queryOptions);
 | 
					options = extend(defaultOptions, options, queryOptions)
 | 
				
			||||||
Reveal.initialize(options);
 | 
					Reveal.initialize(options)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
window.viewAjaxCallback = () => {
 | 
					window.viewAjaxCallback = () => {
 | 
				
			||||||
    Reveal.layout();
 | 
					  Reveal.layout()
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function renderSlide(event) {
 | 
					function renderSlide (event) {
 | 
				
			||||||
    if (window.location.search.match( /print-pdf/gi )) {
 | 
					  if (window.location.search.match(/print-pdf/gi)) {
 | 
				
			||||||
        const slides = $('.slides');
 | 
					    const slides = $('.slides')
 | 
				
			||||||
        var title = document.title;
 | 
					    let title = document.title
 | 
				
			||||||
        finishView(slides);
 | 
					    finishView(slides)
 | 
				
			||||||
        document.title = title;
 | 
					    document.title = title
 | 
				
			||||||
        Reveal.layout();
 | 
					    Reveal.layout()
 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
        const markdown = $(event.currentSlide);
 | 
					    const markdown = $(event.currentSlide)
 | 
				
			||||||
    if (!markdown.attr('data-rendered')) {
 | 
					    if (!markdown.attr('data-rendered')) {
 | 
				
			||||||
            var title = document.title;
 | 
					      let title = document.title
 | 
				
			||||||
            finishView(markdown);
 | 
					      finishView(markdown)
 | 
				
			||||||
            markdown.attr('data-rendered', 'true');
 | 
					      markdown.attr('data-rendered', 'true')
 | 
				
			||||||
            document.title = title;
 | 
					      document.title = title
 | 
				
			||||||
            Reveal.layout();
 | 
					      Reveal.layout()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Reveal.addEventListener('ready', event => {
 | 
					Reveal.addEventListener('ready', event => {
 | 
				
			||||||
    renderSlide(event);
 | 
					  renderSlide(event)
 | 
				
			||||||
    const markdown = $(event.currentSlide);
 | 
					  const markdown = $(event.currentSlide)
 | 
				
			||||||
    // force browser redraw
 | 
					    // force browser redraw
 | 
				
			||||||
  setTimeout(() => {
 | 
					  setTimeout(() => {
 | 
				
			||||||
        markdown.hide().show(0);
 | 
					    markdown.hide().show(0)
 | 
				
			||||||
    }, 0);
 | 
					  }, 0)
 | 
				
			||||||
});
 | 
					})
 | 
				
			||||||
Reveal.addEventListener('slidechanged', renderSlide);
 | 
					Reveal.addEventListener('slidechanged', renderSlide)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const isMacLike = navigator.platform.match(/(Mac|iPhone|iPod|iPad)/i) ? true : false;
 | 
					const isMacLike = !!navigator.platform.match(/(Mac|iPhone|iPod|iPad)/i)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if (!isMacLike) $('.container').addClass('hidescrollbar');
 | 
					if (!isMacLike) $('.container').addClass('hidescrollbar')
 | 
				
			||||||
 | 
				
			|||||||
@ -1,365 +1,367 @@
 | 
				
			|||||||
 | 
					/* eslint-env browser, jquery */
 | 
				
			||||||
 | 
					/* global _ */
 | 
				
			||||||
// Inject line numbers for sync scroll.
 | 
					// Inject line numbers for sync scroll.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import markdownitContainer from 'markdown-it-container';
 | 
					import markdownitContainer from 'markdown-it-container'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { md } from './extra';
 | 
					import { md } from './extra'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function addPart(tokens, idx) {
 | 
					function addPart (tokens, idx) {
 | 
				
			||||||
  if (tokens[idx].map && tokens[idx].level === 0) {
 | 
					  if (tokens[idx].map && tokens[idx].level === 0) {
 | 
				
			||||||
        const startline = tokens[idx].map[0] + 1;
 | 
					    const startline = tokens[idx].map[0] + 1
 | 
				
			||||||
        const endline = tokens[idx].map[1];
 | 
					    const endline = tokens[idx].map[1]
 | 
				
			||||||
        tokens[idx].attrJoin('class', 'part');
 | 
					    tokens[idx].attrJoin('class', 'part')
 | 
				
			||||||
        tokens[idx].attrJoin('data-startline', startline);
 | 
					    tokens[idx].attrJoin('data-startline', startline)
 | 
				
			||||||
        tokens[idx].attrJoin('data-endline', endline);
 | 
					    tokens[idx].attrJoin('data-endline', endline)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
md.renderer.rules.blockquote_open = function (tokens, idx, options, env, self) {
 | 
					md.renderer.rules.blockquote_open = function (tokens, idx, options, env, self) {
 | 
				
			||||||
    tokens[idx].attrJoin('class', 'raw');
 | 
					  tokens[idx].attrJoin('class', 'raw')
 | 
				
			||||||
    addPart(tokens, idx);
 | 
					  addPart(tokens, idx)
 | 
				
			||||||
    return self.renderToken(...arguments);
 | 
					  return self.renderToken(...arguments)
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
md.renderer.rules.table_open = function (tokens, idx, options, env, self) {
 | 
					md.renderer.rules.table_open = function (tokens, idx, options, env, self) {
 | 
				
			||||||
    addPart(tokens, idx);
 | 
					  addPart(tokens, idx)
 | 
				
			||||||
    return self.renderToken(...arguments);
 | 
					  return self.renderToken(...arguments)
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
md.renderer.rules.bullet_list_open = function (tokens, idx, options, env, self) {
 | 
					md.renderer.rules.bullet_list_open = function (tokens, idx, options, env, self) {
 | 
				
			||||||
    addPart(tokens, idx);
 | 
					  addPart(tokens, idx)
 | 
				
			||||||
    return self.renderToken(...arguments);
 | 
					  return self.renderToken(...arguments)
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
md.renderer.rules.list_item_open = function (tokens, idx, options, env, self) {
 | 
					md.renderer.rules.list_item_open = function (tokens, idx, options, env, self) {
 | 
				
			||||||
    tokens[idx].attrJoin('class', 'raw');
 | 
					  tokens[idx].attrJoin('class', 'raw')
 | 
				
			||||||
  if (tokens[idx].map) {
 | 
					  if (tokens[idx].map) {
 | 
				
			||||||
        const startline = tokens[idx].map[0] + 1;
 | 
					    const startline = tokens[idx].map[0] + 1
 | 
				
			||||||
        const endline = tokens[idx].map[1];
 | 
					    const endline = tokens[idx].map[1]
 | 
				
			||||||
        tokens[idx].attrJoin('data-startline', startline);
 | 
					    tokens[idx].attrJoin('data-startline', startline)
 | 
				
			||||||
        tokens[idx].attrJoin('data-endline', endline);
 | 
					    tokens[idx].attrJoin('data-endline', endline)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
    return self.renderToken(...arguments);
 | 
					  return self.renderToken(...arguments)
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
md.renderer.rules.ordered_list_open = function (tokens, idx, options, env, self) {
 | 
					md.renderer.rules.ordered_list_open = function (tokens, idx, options, env, self) {
 | 
				
			||||||
    addPart(tokens, idx);
 | 
					  addPart(tokens, idx)
 | 
				
			||||||
    return self.renderToken(...arguments);
 | 
					  return self.renderToken(...arguments)
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
md.renderer.rules.link_open = function (tokens, idx, options, env, self) {
 | 
					md.renderer.rules.link_open = function (tokens, idx, options, env, self) {
 | 
				
			||||||
    addPart(tokens, idx);
 | 
					  addPart(tokens, idx)
 | 
				
			||||||
    return self.renderToken(...arguments);
 | 
					  return self.renderToken(...arguments)
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
md.renderer.rules.paragraph_open = function (tokens, idx, options, env, self) {
 | 
					md.renderer.rules.paragraph_open = function (tokens, idx, options, env, self) {
 | 
				
			||||||
    addPart(tokens, idx);
 | 
					  addPart(tokens, idx)
 | 
				
			||||||
    return self.renderToken(...arguments);
 | 
					  return self.renderToken(...arguments)
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
md.renderer.rules.heading_open = function (tokens, idx, options, env, self) {
 | 
					md.renderer.rules.heading_open = function (tokens, idx, options, env, self) {
 | 
				
			||||||
    tokens[idx].attrJoin('class', 'raw');
 | 
					  tokens[idx].attrJoin('class', 'raw')
 | 
				
			||||||
    addPart(tokens, idx);
 | 
					  addPart(tokens, idx)
 | 
				
			||||||
    return self.renderToken(...arguments);
 | 
					  return self.renderToken(...arguments)
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
md.renderer.rules.fence = (tokens, idx, options, env, self) => {
 | 
					md.renderer.rules.fence = (tokens, idx, options, env, self) => {
 | 
				
			||||||
    const token = tokens[idx];
 | 
					  const token = tokens[idx]
 | 
				
			||||||
    const info = token.info ? md.utils.unescapeAll(token.info).trim() : '';
 | 
					  const info = token.info ? md.utils.unescapeAll(token.info).trim() : ''
 | 
				
			||||||
    let langName = '';
 | 
					  let langName = ''
 | 
				
			||||||
    let highlighted;
 | 
					  let highlighted
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (info) {
 | 
					  if (info) {
 | 
				
			||||||
        langName = info.split(/\s+/g)[0];
 | 
					    langName = info.split(/\s+/g)[0]
 | 
				
			||||||
        if (/\!$/.test(info)) token.attrJoin('class', 'wrap');
 | 
					    if (/!$/.test(info)) token.attrJoin('class', 'wrap')
 | 
				
			||||||
        token.attrJoin('class', options.langPrefix + langName.replace(/\=$|\=\d+$|\=\+$|\!$|\=\!/, ''));
 | 
					    token.attrJoin('class', options.langPrefix + langName.replace(/=$|=\d+$|=\+$|!$|=!/, ''))
 | 
				
			||||||
        token.attrJoin('class', 'hljs');
 | 
					    token.attrJoin('class', 'hljs')
 | 
				
			||||||
        token.attrJoin('class', 'raw');
 | 
					    token.attrJoin('class', 'raw')
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (options.highlight) {
 | 
					  if (options.highlight) {
 | 
				
			||||||
        highlighted = options.highlight(token.content, langName) || md.utils.escapeHtml(token.content);
 | 
					    highlighted = options.highlight(token.content, langName) || md.utils.escapeHtml(token.content)
 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
        highlighted = md.utils.escapeHtml(token.content);
 | 
					    highlighted = md.utils.escapeHtml(token.content)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (highlighted.indexOf('<pre') === 0) {
 | 
					  if (highlighted.indexOf('<pre') === 0) {
 | 
				
			||||||
        return `${highlighted}\n`;
 | 
					    return `${highlighted}\n`
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (tokens[idx].map && tokens[idx].level === 0) {
 | 
					  if (tokens[idx].map && tokens[idx].level === 0) {
 | 
				
			||||||
        const startline = tokens[idx].map[0] + 1;
 | 
					    const startline = tokens[idx].map[0] + 1
 | 
				
			||||||
        const endline = tokens[idx].map[1];
 | 
					    const endline = tokens[idx].map[1]
 | 
				
			||||||
        return `<pre class="part" data-startline="${startline}" data-endline="${endline}"><code${self.renderAttrs(token)}>${highlighted}</code></pre>\n`;
 | 
					    return `<pre class="part" data-startline="${startline}" data-endline="${endline}"><code${self.renderAttrs(token)}>${highlighted}</code></pre>\n`
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return `<pre><code${self.renderAttrs(token)}>${highlighted}</code></pre>\n`;
 | 
					  return `<pre><code${self.renderAttrs(token)}>${highlighted}</code></pre>\n`
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
md.renderer.rules.code_block = (tokens, idx, options, env, self) => {
 | 
					md.renderer.rules.code_block = (tokens, idx, options, env, self) => {
 | 
				
			||||||
  if (tokens[idx].map && tokens[idx].level === 0) {
 | 
					  if (tokens[idx].map && tokens[idx].level === 0) {
 | 
				
			||||||
        const startline = tokens[idx].map[0] + 1;
 | 
					    const startline = tokens[idx].map[0] + 1
 | 
				
			||||||
        const endline = tokens[idx].map[1];
 | 
					    const endline = tokens[idx].map[1]
 | 
				
			||||||
        return `<pre class="part" data-startline="${startline}" data-endline="${endline}"><code>${md.utils.escapeHtml(tokens[idx].content)}</code></pre>\n`;
 | 
					    return `<pre class="part" data-startline="${startline}" data-endline="${endline}"><code>${md.utils.escapeHtml(tokens[idx].content)}</code></pre>\n`
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
    return `<pre><code>${md.utils.escapeHtml(tokens[idx].content)}</code></pre>\n`;
 | 
					  return `<pre><code>${md.utils.escapeHtml(tokens[idx].content)}</code></pre>\n`
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
function renderContainer(tokens, idx, options, env, self) {
 | 
					function renderContainer (tokens, idx, options, env, self) {
 | 
				
			||||||
    tokens[idx].attrJoin('role', 'alert');
 | 
					  tokens[idx].attrJoin('role', 'alert')
 | 
				
			||||||
    tokens[idx].attrJoin('class', 'alert');
 | 
					  tokens[idx].attrJoin('class', 'alert')
 | 
				
			||||||
    tokens[idx].attrJoin('class', `alert-${tokens[idx].info.trim()}`);
 | 
					  tokens[idx].attrJoin('class', `alert-${tokens[idx].info.trim()}`)
 | 
				
			||||||
    addPart(tokens, idx);
 | 
					  addPart(tokens, idx)
 | 
				
			||||||
    return self.renderToken(...arguments);
 | 
					  return self.renderToken(...arguments)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
md.use(markdownitContainer, 'success', { render: renderContainer });
 | 
					md.use(markdownitContainer, 'success', { render: renderContainer })
 | 
				
			||||||
md.use(markdownitContainer, 'info', { render: renderContainer });
 | 
					md.use(markdownitContainer, 'info', { render: renderContainer })
 | 
				
			||||||
md.use(markdownitContainer, 'warning', { render: renderContainer });
 | 
					md.use(markdownitContainer, 'warning', { render: renderContainer })
 | 
				
			||||||
md.use(markdownitContainer, 'danger', { render: renderContainer });
 | 
					md.use(markdownitContainer, 'danger', { render: renderContainer })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// FIXME: expose syncscroll to window
 | 
					// FIXME: expose syncscroll to window
 | 
				
			||||||
window.syncscroll = true;
 | 
					window.syncscroll = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
window.preventSyncScrollToEdit = false;
 | 
					window.preventSyncScrollToEdit = false
 | 
				
			||||||
window.preventSyncScrollToView = false;
 | 
					window.preventSyncScrollToView = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const editScrollThrottle = 5;
 | 
					const editScrollThrottle = 5
 | 
				
			||||||
const viewScrollThrottle = 5;
 | 
					const viewScrollThrottle = 5
 | 
				
			||||||
const buildMapThrottle = 100;
 | 
					const buildMapThrottle = 100
 | 
				
			||||||
 | 
					
 | 
				
			||||||
let viewScrolling = false;
 | 
					let viewScrolling = false
 | 
				
			||||||
let editScrolling = false;
 | 
					let editScrolling = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
let editArea = null;
 | 
					let editArea = null
 | 
				
			||||||
let viewArea = null;
 | 
					let viewArea = null
 | 
				
			||||||
let markdownArea = null;
 | 
					let markdownArea = null
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function setupSyncAreas(edit, view, markdown) {
 | 
					export function setupSyncAreas (edit, view, markdown) {
 | 
				
			||||||
    editArea = edit;
 | 
					  editArea = edit
 | 
				
			||||||
    viewArea = view;
 | 
					  viewArea = view
 | 
				
			||||||
    markdownArea = markdown;
 | 
					  markdownArea = markdown
 | 
				
			||||||
    editArea.on('scroll', _.throttle(syncScrollToView, editScrollThrottle));
 | 
					  editArea.on('scroll', _.throttle(syncScrollToView, editScrollThrottle))
 | 
				
			||||||
    viewArea.on('scroll', _.throttle(syncScrollToEdit, viewScrollThrottle));
 | 
					  viewArea.on('scroll', _.throttle(syncScrollToEdit, viewScrollThrottle))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
let scrollMap, lineHeightMap, viewTop, viewBottom;
 | 
					let scrollMap, lineHeightMap, viewTop, viewBottom
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function clearMap() {
 | 
					export function clearMap () {
 | 
				
			||||||
    scrollMap = null;
 | 
					  scrollMap = null
 | 
				
			||||||
    lineHeightMap = null;
 | 
					  lineHeightMap = null
 | 
				
			||||||
    viewTop = null;
 | 
					  viewTop = null
 | 
				
			||||||
    viewBottom = null;
 | 
					  viewBottom = null
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
window.viewAjaxCallback = clearMap;
 | 
					window.viewAjaxCallback = clearMap
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const buildMap = _.throttle(buildMapInner, buildMapThrottle);
 | 
					const buildMap = _.throttle(buildMapInner, buildMapThrottle)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Build offsets for each line (lines can be wrapped)
 | 
					// Build offsets for each line (lines can be wrapped)
 | 
				
			||||||
// That's a bit dirty to process each line everytime, but ok for demo.
 | 
					// That's a bit dirty to process each line everytime, but ok for demo.
 | 
				
			||||||
// Optimizations are required only for big texts.
 | 
					// Optimizations are required only for big texts.
 | 
				
			||||||
function buildMapInner(callback) {
 | 
					function buildMapInner (callback) {
 | 
				
			||||||
    if (!viewArea || !markdownArea) return;
 | 
					  if (!viewArea || !markdownArea) return
 | 
				
			||||||
    let i, offset, nonEmptyList, pos, a, b, _lineHeightMap, linesCount, acc, _scrollMap;
 | 
					  let i, offset, nonEmptyList, pos, a, b, _lineHeightMap, linesCount, acc, _scrollMap
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    offset = viewArea.scrollTop() - viewArea.offset().top;
 | 
					  offset = viewArea.scrollTop() - viewArea.offset().top
 | 
				
			||||||
    _scrollMap = [];
 | 
					  _scrollMap = []
 | 
				
			||||||
    nonEmptyList = [];
 | 
					  nonEmptyList = []
 | 
				
			||||||
    _lineHeightMap = [];
 | 
					  _lineHeightMap = []
 | 
				
			||||||
    viewTop = 0;
 | 
					  viewTop = 0
 | 
				
			||||||
    viewBottom = viewArea[0].scrollHeight - viewArea.height();
 | 
					  viewBottom = viewArea[0].scrollHeight - viewArea.height()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    acc = 0;
 | 
					  acc = 0
 | 
				
			||||||
    const lines = editor.getValue().split('\n');
 | 
					  const lines = window.editor.getValue().split('\n')
 | 
				
			||||||
    const lineHeight = editor.defaultTextHeight();
 | 
					  const lineHeight = window.editor.defaultTextHeight()
 | 
				
			||||||
  for (i = 0; i < lines.length; i++) {
 | 
					  for (i = 0; i < lines.length; i++) {
 | 
				
			||||||
        const str = lines[i];
 | 
					    const str = lines[i]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        _lineHeightMap.push(acc);
 | 
					    _lineHeightMap.push(acc)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (str.length === 0) {
 | 
					    if (str.length === 0) {
 | 
				
			||||||
            acc++;
 | 
					      acc++
 | 
				
			||||||
            continue;
 | 
					      continue
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const h = editor.heightAtLine(i + 1) - editor.heightAtLine(i);
 | 
					    const h = window.editor.heightAtLine(i + 1) - window.editor.heightAtLine(i)
 | 
				
			||||||
        acc += Math.round(h / lineHeight);
 | 
					    acc += Math.round(h / lineHeight)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
    _lineHeightMap.push(acc);
 | 
					  _lineHeightMap.push(acc)
 | 
				
			||||||
    linesCount = acc;
 | 
					  linesCount = acc
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  for (i = 0; i < linesCount; i++) {
 | 
					  for (i = 0; i < linesCount; i++) {
 | 
				
			||||||
        _scrollMap.push(-1);
 | 
					    _scrollMap.push(-1)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    nonEmptyList.push(0);
 | 
					  nonEmptyList.push(0)
 | 
				
			||||||
    // make the first line go top
 | 
					    // make the first line go top
 | 
				
			||||||
    _scrollMap[0] = viewTop;
 | 
					  _scrollMap[0] = viewTop
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const parts = markdownArea.find('.part').toArray();
 | 
					  const parts = markdownArea.find('.part').toArray()
 | 
				
			||||||
  for (i = 0; i < parts.length; i++) {
 | 
					  for (i = 0; i < parts.length; i++) {
 | 
				
			||||||
        const $el = $(parts[i]);
 | 
					    const $el = $(parts[i])
 | 
				
			||||||
        let t = $el.attr('data-startline') - 1;
 | 
					    let t = $el.attr('data-startline') - 1
 | 
				
			||||||
    if (t === '') {
 | 
					    if (t === '') {
 | 
				
			||||||
            return;
 | 
					      return
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
        t = _lineHeightMap[t];
 | 
					    t = _lineHeightMap[t]
 | 
				
			||||||
    if (t !== 0 && t !== nonEmptyList[nonEmptyList.length - 1]) {
 | 
					    if (t !== 0 && t !== nonEmptyList[nonEmptyList.length - 1]) {
 | 
				
			||||||
            nonEmptyList.push(t);
 | 
					      nonEmptyList.push(t)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
        _scrollMap[t] = Math.round($el.offset().top + offset - 10);
 | 
					    _scrollMap[t] = Math.round($el.offset().top + offset - 10)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    nonEmptyList.push(linesCount);
 | 
					  nonEmptyList.push(linesCount)
 | 
				
			||||||
    _scrollMap[linesCount] = viewArea[0].scrollHeight;
 | 
					  _scrollMap[linesCount] = viewArea[0].scrollHeight
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pos = 0;
 | 
					  pos = 0
 | 
				
			||||||
  for (i = 1; i < linesCount; i++) {
 | 
					  for (i = 1; i < linesCount; i++) {
 | 
				
			||||||
    if (_scrollMap[i] !== -1) {
 | 
					    if (_scrollMap[i] !== -1) {
 | 
				
			||||||
            pos++;
 | 
					      pos++
 | 
				
			||||||
            continue;
 | 
					      continue
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        a = nonEmptyList[pos];
 | 
					    a = nonEmptyList[pos]
 | 
				
			||||||
        b = nonEmptyList[pos + 1];
 | 
					    b = nonEmptyList[pos + 1]
 | 
				
			||||||
        _scrollMap[i] = Math.round((_scrollMap[b] * (i - a) + _scrollMap[a] * (b - i)) / (b - a));
 | 
					    _scrollMap[i] = Math.round((_scrollMap[b] * (i - a) + _scrollMap[a] * (b - i)) / (b - a))
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    _scrollMap[0] = 0;
 | 
					  _scrollMap[0] = 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    scrollMap = _scrollMap;
 | 
					  scrollMap = _scrollMap
 | 
				
			||||||
    lineHeightMap = _lineHeightMap;
 | 
					  lineHeightMap = _lineHeightMap
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (loaded && callback) callback();
 | 
					  if (window.loaded && callback) callback()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// sync view scroll progress to edit
 | 
					// sync view scroll progress to edit
 | 
				
			||||||
let viewScrollingTimer = null;
 | 
					let viewScrollingTimer = null
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function syncScrollToEdit(event, preventAnimate) {
 | 
					export function syncScrollToEdit (event, preventAnimate) {
 | 
				
			||||||
    if (currentMode != modeType.both || !syncscroll || !editArea) return;
 | 
					  if (window.currentMode !== window.modeType.both || !window.syncscroll || !editArea) return
 | 
				
			||||||
    if (preventSyncScrollToEdit) {
 | 
					  if (window.preventSyncScrollToEdit) {
 | 
				
			||||||
        if (typeof preventSyncScrollToEdit === 'number') {
 | 
					    if (typeof window.preventSyncScrollToEdit === 'number') {
 | 
				
			||||||
            preventSyncScrollToEdit--;
 | 
					      window.preventSyncScrollToEdit--
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
            preventSyncScrollToEdit = false;
 | 
					      window.preventSyncScrollToEdit = false
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
        return;
 | 
					    return
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  if (!scrollMap || !lineHeightMap) {
 | 
					  if (!scrollMap || !lineHeightMap) {
 | 
				
			||||||
    buildMap(() => {
 | 
					    buildMap(() => {
 | 
				
			||||||
            syncScrollToEdit(event, preventAnimate);
 | 
					      syncScrollToEdit(event, preventAnimate)
 | 
				
			||||||
        });
 | 
					    })
 | 
				
			||||||
        return;
 | 
					    return
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
    if (editScrolling) return;
 | 
					  if (editScrolling) return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const scrollTop = viewArea[0].scrollTop;
 | 
					  const scrollTop = viewArea[0].scrollTop
 | 
				
			||||||
    let lineIndex = 0;
 | 
					  let lineIndex = 0
 | 
				
			||||||
    for (var i = 0, l = scrollMap.length; i < l; i++) {
 | 
					  for (let i = 0, l = scrollMap.length; i < l; i++) {
 | 
				
			||||||
    if (scrollMap[i] > scrollTop) {
 | 
					    if (scrollMap[i] > scrollTop) {
 | 
				
			||||||
            break;
 | 
					      break
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
            lineIndex = i;
 | 
					      lineIndex = i
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
    let lineNo = 0;
 | 
					  let lineNo = 0
 | 
				
			||||||
    let lineDiff = 0;
 | 
					  let lineDiff = 0
 | 
				
			||||||
    for (var i = 0, l = lineHeightMap.length; i < l; i++) {
 | 
					  for (let i = 0, l = lineHeightMap.length; i < l; i++) {
 | 
				
			||||||
    if (lineHeightMap[i] > lineIndex) {
 | 
					    if (lineHeightMap[i] > lineIndex) {
 | 
				
			||||||
            break;
 | 
					      break
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
            lineNo = lineHeightMap[i];
 | 
					      lineNo = lineHeightMap[i]
 | 
				
			||||||
            lineDiff = lineHeightMap[i + 1] - lineNo;
 | 
					      lineDiff = lineHeightMap[i + 1] - lineNo
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let posTo = 0;
 | 
					  let posTo = 0
 | 
				
			||||||
    let topDiffPercent = 0;
 | 
					  let topDiffPercent = 0
 | 
				
			||||||
    let posToNextDiff = 0;
 | 
					  let posToNextDiff = 0
 | 
				
			||||||
    const scrollInfo = editor.getScrollInfo();
 | 
					  const scrollInfo = window.editor.getScrollInfo()
 | 
				
			||||||
    const textHeight = editor.defaultTextHeight();
 | 
					  const textHeight = window.editor.defaultTextHeight()
 | 
				
			||||||
    const preLastLineHeight = scrollInfo.height - scrollInfo.clientHeight - textHeight;
 | 
					  const preLastLineHeight = scrollInfo.height - scrollInfo.clientHeight - textHeight
 | 
				
			||||||
    const preLastLineNo = Math.round(preLastLineHeight / textHeight);
 | 
					  const preLastLineNo = Math.round(preLastLineHeight / textHeight)
 | 
				
			||||||
    const preLastLinePos = scrollMap[preLastLineNo];
 | 
					  const preLastLinePos = scrollMap[preLastLineNo]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (scrollInfo.height > scrollInfo.clientHeight && scrollTop >= preLastLinePos) {
 | 
					  if (scrollInfo.height > scrollInfo.clientHeight && scrollTop >= preLastLinePos) {
 | 
				
			||||||
        posTo = preLastLineHeight;
 | 
					    posTo = preLastLineHeight
 | 
				
			||||||
        topDiffPercent = (scrollTop - preLastLinePos) / (viewBottom - preLastLinePos);
 | 
					    topDiffPercent = (scrollTop - preLastLinePos) / (viewBottom - preLastLinePos)
 | 
				
			||||||
        posToNextDiff = textHeight * topDiffPercent;
 | 
					    posToNextDiff = textHeight * topDiffPercent
 | 
				
			||||||
        posTo += Math.ceil(posToNextDiff);
 | 
					    posTo += Math.ceil(posToNextDiff)
 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
        posTo = lineNo * textHeight;
 | 
					    posTo = lineNo * textHeight
 | 
				
			||||||
        topDiffPercent = (scrollTop - scrollMap[lineNo]) / (scrollMap[lineNo + lineDiff] - scrollMap[lineNo]);
 | 
					    topDiffPercent = (scrollTop - scrollMap[lineNo]) / (scrollMap[lineNo + lineDiff] - scrollMap[lineNo])
 | 
				
			||||||
        posToNextDiff = textHeight * lineDiff * topDiffPercent;
 | 
					    posToNextDiff = textHeight * lineDiff * topDiffPercent
 | 
				
			||||||
        posTo += Math.ceil(posToNextDiff);
 | 
					    posTo += Math.ceil(posToNextDiff)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (preventAnimate) {
 | 
					  if (preventAnimate) {
 | 
				
			||||||
        editArea.scrollTop(posTo);
 | 
					    editArea.scrollTop(posTo)
 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
        const posDiff = Math.abs(scrollInfo.top - posTo);
 | 
					    const posDiff = Math.abs(scrollInfo.top - posTo)
 | 
				
			||||||
        var duration = posDiff / 50;
 | 
					    var duration = posDiff / 50
 | 
				
			||||||
        duration = duration >= 100 ? duration : 100;
 | 
					    duration = duration >= 100 ? duration : 100
 | 
				
			||||||
    editArea.stop(true, true).animate({
 | 
					    editArea.stop(true, true).animate({
 | 
				
			||||||
      scrollTop: posTo
 | 
					      scrollTop: posTo
 | 
				
			||||||
        }, duration, "linear");
 | 
					    }, duration, 'linear')
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    viewScrolling = true;
 | 
					  viewScrolling = true
 | 
				
			||||||
    clearTimeout(viewScrollingTimer);
 | 
					  clearTimeout(viewScrollingTimer)
 | 
				
			||||||
    viewScrollingTimer = setTimeout(viewScrollingTimeoutInner, duration * 1.5);
 | 
					  viewScrollingTimer = setTimeout(viewScrollingTimeoutInner, duration * 1.5)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function viewScrollingTimeoutInner() {
 | 
					function viewScrollingTimeoutInner () {
 | 
				
			||||||
    viewScrolling = false;
 | 
					  viewScrolling = false
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// sync edit scroll progress to view
 | 
					// sync edit scroll progress to view
 | 
				
			||||||
let editScrollingTimer = null;
 | 
					let editScrollingTimer = null
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function syncScrollToView(event, preventAnimate) {
 | 
					export function syncScrollToView (event, preventAnimate) {
 | 
				
			||||||
    if (currentMode != modeType.both || !syncscroll || !viewArea) return;
 | 
					  if (window.currentMode !== window.modeType.both || !window.syncscroll || !viewArea) return
 | 
				
			||||||
    if (preventSyncScrollToView) {
 | 
					  if (window.preventSyncScrollToView) {
 | 
				
			||||||
    if (typeof preventSyncScrollToView === 'number') {
 | 
					    if (typeof preventSyncScrollToView === 'number') {
 | 
				
			||||||
            preventSyncScrollToView--;
 | 
					      window.preventSyncScrollToView--
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
            preventSyncScrollToView = false;
 | 
					      window.preventSyncScrollToView = false
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
        return;
 | 
					    return
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  if (!scrollMap || !lineHeightMap) {
 | 
					  if (!scrollMap || !lineHeightMap) {
 | 
				
			||||||
    buildMap(() => {
 | 
					    buildMap(() => {
 | 
				
			||||||
            syncScrollToView(event, preventAnimate);
 | 
					      syncScrollToView(event, preventAnimate)
 | 
				
			||||||
        });
 | 
					    })
 | 
				
			||||||
        return;
 | 
					    return
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
    if (viewScrolling) return;
 | 
					  if (viewScrolling) return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let lineNo, posTo;
 | 
					  let lineNo, posTo
 | 
				
			||||||
    let topDiffPercent, posToNextDiff;
 | 
					  let topDiffPercent, posToNextDiff
 | 
				
			||||||
    const scrollInfo = editor.getScrollInfo();
 | 
					  const scrollInfo = window.editor.getScrollInfo()
 | 
				
			||||||
    const textHeight = editor.defaultTextHeight();
 | 
					  const textHeight = window.editor.defaultTextHeight()
 | 
				
			||||||
    lineNo = Math.floor(scrollInfo.top / textHeight);
 | 
					  lineNo = Math.floor(scrollInfo.top / textHeight)
 | 
				
			||||||
    // if reach the last line, will start lerp to the bottom
 | 
					    // if reach the last line, will start lerp to the bottom
 | 
				
			||||||
    const diffToBottom = (scrollInfo.top + scrollInfo.clientHeight) - (scrollInfo.height - textHeight);
 | 
					  const diffToBottom = (scrollInfo.top + scrollInfo.clientHeight) - (scrollInfo.height - textHeight)
 | 
				
			||||||
  if (scrollInfo.height > scrollInfo.clientHeight && diffToBottom > 0) {
 | 
					  if (scrollInfo.height > scrollInfo.clientHeight && diffToBottom > 0) {
 | 
				
			||||||
        topDiffPercent = diffToBottom / textHeight;
 | 
					    topDiffPercent = diffToBottom / textHeight
 | 
				
			||||||
        posTo = scrollMap[lineNo + 1];
 | 
					    posTo = scrollMap[lineNo + 1]
 | 
				
			||||||
        posToNextDiff = (viewBottom - posTo) * topDiffPercent;
 | 
					    posToNextDiff = (viewBottom - posTo) * topDiffPercent
 | 
				
			||||||
        posTo += Math.floor(posToNextDiff);
 | 
					    posTo += Math.floor(posToNextDiff)
 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
        topDiffPercent = (scrollInfo.top % textHeight) / textHeight;
 | 
					    topDiffPercent = (scrollInfo.top % textHeight) / textHeight
 | 
				
			||||||
        posTo = scrollMap[lineNo];
 | 
					    posTo = scrollMap[lineNo]
 | 
				
			||||||
        posToNextDiff = (scrollMap[lineNo + 1] - posTo) * topDiffPercent;
 | 
					    posToNextDiff = (scrollMap[lineNo + 1] - posTo) * topDiffPercent
 | 
				
			||||||
        posTo += Math.floor(posToNextDiff);
 | 
					    posTo += Math.floor(posToNextDiff)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (preventAnimate) {
 | 
					  if (preventAnimate) {
 | 
				
			||||||
        viewArea.scrollTop(posTo);
 | 
					    viewArea.scrollTop(posTo)
 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
        const posDiff = Math.abs(viewArea.scrollTop() - posTo);
 | 
					    const posDiff = Math.abs(viewArea.scrollTop() - posTo)
 | 
				
			||||||
        var duration = posDiff / 50;
 | 
					    var duration = posDiff / 50
 | 
				
			||||||
        duration = duration >= 100 ? duration : 100;
 | 
					    duration = duration >= 100 ? duration : 100
 | 
				
			||||||
    viewArea.stop(true, true).animate({
 | 
					    viewArea.stop(true, true).animate({
 | 
				
			||||||
      scrollTop: posTo
 | 
					      scrollTop: posTo
 | 
				
			||||||
        }, duration, "linear");
 | 
					    }, duration, 'linear')
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    editScrolling = true;
 | 
					  editScrolling = true
 | 
				
			||||||
    clearTimeout(editScrollingTimer);
 | 
					  clearTimeout(editScrollingTimer)
 | 
				
			||||||
    editScrollingTimer = setTimeout(editScrollingTimeoutInner, duration * 1.5);
 | 
					  editScrollingTimer = setTimeout(editScrollingTimeoutInner, duration * 1.5)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function editScrollingTimeoutInner() {
 | 
					function editScrollingTimeoutInner () {
 | 
				
			||||||
    editScrolling = false;
 | 
					  editScrolling = false
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										140
									
								
								public/vendor/md-toc.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										140
									
								
								public/vendor/md-toc.js
									
									
									
									
										vendored
									
									
								
							@ -1,129 +1,123 @@
 | 
				
			|||||||
 | 
					/* eslint-env browser, jquery */
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * md-toc.js v1.0.2
 | 
					 * md-toc.js v1.0.2
 | 
				
			||||||
 * https://github.com/yijian166/md-toc.js
 | 
					 * https://github.com/yijian166/md-toc.js
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
(function (window) {
 | 
					(function (window) {
 | 
				
			||||||
    function Toc(id, options) {
 | 
					  function Toc (id, options) {
 | 
				
			||||||
        this.el = document.getElementById(id);
 | 
					    this.el = document.getElementById(id)
 | 
				
			||||||
        if (!this.el) return;
 | 
					    if (!this.el) return
 | 
				
			||||||
        this.options = options || {};
 | 
					    this.options = options || {}
 | 
				
			||||||
        this.tocLevel = parseInt(options.level) || 0;
 | 
					    this.tocLevel = parseInt(options.level) || 0
 | 
				
			||||||
        this.tocClass = options['class'] || 'toc';
 | 
					    this.tocClass = options['class'] || 'toc'
 | 
				
			||||||
        this.ulClass = options['ulClass'];
 | 
					    this.ulClass = options['ulClass']
 | 
				
			||||||
        this.tocTop = parseInt(options.top) || 0;
 | 
					    this.tocTop = parseInt(options.top) || 0
 | 
				
			||||||
        this.elChilds = this.el.children;
 | 
					    this.elChilds = this.el.children
 | 
				
			||||||
        this.process = options['process'];
 | 
					    this.process = options['process']
 | 
				
			||||||
        if (!this.elChilds.length) return;
 | 
					    if (!this.elChilds.length) return
 | 
				
			||||||
        this._init();
 | 
					    this._init()
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  Toc.prototype._init = function () {
 | 
					  Toc.prototype._init = function () {
 | 
				
			||||||
        this._collectTitleElements();
 | 
					    this._collectTitleElements()
 | 
				
			||||||
        this._createTocContent();
 | 
					    this._createTocContent()
 | 
				
			||||||
        this._showToc();
 | 
					    this._showToc()
 | 
				
			||||||
    };
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  Toc.prototype._collectTitleElements = function () {
 | 
					  Toc.prototype._collectTitleElements = function () {
 | 
				
			||||||
        this._elTitlesNames = [],
 | 
					    this._elTitlesNames = []
 | 
				
			||||||
            this.elTitleElements = [];
 | 
					    this.elTitleElements = []
 | 
				
			||||||
    for (var i = 1; i < 7; i++) {
 | 
					    for (var i = 1; i < 7; i++) {
 | 
				
			||||||
      if (this.el.getElementsByTagName('h' + i).length) {
 | 
					      if (this.el.getElementsByTagName('h' + i).length) {
 | 
				
			||||||
                this._elTitlesNames.push('h' + i);
 | 
					        this._elTitlesNames.push('h' + i)
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this._elTitlesNames.length = this._elTitlesNames.length > this.tocLevel ? this.tocLevel : this._elTitlesNames.length;
 | 
					    this._elTitlesNames.length = this._elTitlesNames.length > this.tocLevel ? this.tocLevel : this._elTitlesNames.length
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for (var j = 0; j < this.elChilds.length; j++) {
 | 
					    for (var j = 0; j < this.elChilds.length; j++) {
 | 
				
			||||||
            this._elChildName = this.elChilds[j].tagName.toLowerCase();
 | 
					      this._elChildName = this.elChilds[j].tagName.toLowerCase()
 | 
				
			||||||
      if (this._elTitlesNames.toString().match(this._elChildName)) {
 | 
					      if (this._elTitlesNames.toString().match(this._elChildName)) {
 | 
				
			||||||
                this.elTitleElements.push(this.elChilds[j]);
 | 
					        this.elTitleElements.push(this.elChilds[j])
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  Toc.prototype._createTocContent = function () {
 | 
					  Toc.prototype._createTocContent = function () {
 | 
				
			||||||
        this._elTitleElementsLen = this.elTitleElements.length;
 | 
					    this._elTitleElementsLen = this.elTitleElements.length
 | 
				
			||||||
        if (!this._elTitleElementsLen) return;
 | 
					    if (!this._elTitleElementsLen) return
 | 
				
			||||||
        this.tocContent = '';
 | 
					    this.tocContent = ''
 | 
				
			||||||
        this._tempLists = [];
 | 
					    this._tempLists = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        var url = location.origin + location.pathname;
 | 
					 | 
				
			||||||
    for (var i = 0; i < this._elTitleElementsLen; i++) {
 | 
					    for (var i = 0; i < this._elTitleElementsLen; i++) {
 | 
				
			||||||
            var j = i + 1;
 | 
					      var j = i + 1
 | 
				
			||||||
            this._elTitleElement = this.elTitleElements[i];
 | 
					      this._elTitleElement = this.elTitleElements[i]
 | 
				
			||||||
            this._elTitleElementName = this._elTitleElement.tagName;
 | 
					      this._elTitleElementName = this._elTitleElement.tagName
 | 
				
			||||||
            this._elTitleElementText = (typeof this.process === 'function' ? this.process(this._elTitleElement) : this._elTitleElement.innerHTML).replace(/<(?:.|\n)*?>/gm, '');
 | 
					      this._elTitleElementText = (typeof this.process === 'function' ? this.process(this._elTitleElement) : this._elTitleElement.innerHTML).replace(/<(?:.|\n)*?>/gm, '')
 | 
				
			||||||
            var id = this._elTitleElement.getAttribute('id');
 | 
					      var id = this._elTitleElement.getAttribute('id')
 | 
				
			||||||
      if (!id) {
 | 
					      if (!id) {
 | 
				
			||||||
                this._elTitleElement.setAttribute('id', 'tip' + i);
 | 
					        this._elTitleElement.setAttribute('id', 'tip' + i)
 | 
				
			||||||
                id = '#tip' + i;
 | 
					        id = '#tip' + i
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
                id = '#' + id;
 | 
					        id = '#' + id
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            this.tocContent += '<li><a href="' + id + '">' + this._elTitleElementText + '</a>';
 | 
					      this.tocContent += '<li><a href="' + id + '">' + this._elTitleElementText + '</a>'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (j != this._elTitleElementsLen) {
 | 
					      if (j !== this._elTitleElementsLen) {
 | 
				
			||||||
                this._elNextTitleElementName = this.elTitleElements[j].tagName;
 | 
					        this._elNextTitleElementName = this.elTitleElements[j].tagName
 | 
				
			||||||
                if (this._elTitleElementName != this._elNextTitleElementName) {
 | 
					        if (this._elTitleElementName !== this._elNextTitleElementName) {
 | 
				
			||||||
                    var checkColse = false,
 | 
					          var checkColse = false
 | 
				
			||||||
                        y = 1;
 | 
					          var y = 1
 | 
				
			||||||
          for (var t = this._tempLists.length - 1; t >= 0; t--) {
 | 
					          for (var t = this._tempLists.length - 1; t >= 0; t--) {
 | 
				
			||||||
                        if (this._tempLists[t].tagName == this._elNextTitleElementName) {
 | 
					            if (this._tempLists[t].tagName === this._elNextTitleElementName) {
 | 
				
			||||||
                            checkColse = true;
 | 
					              checkColse = true
 | 
				
			||||||
                            break;
 | 
					              break
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
                        y++;
 | 
					            y++
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
          if (checkColse) {
 | 
					          if (checkColse) {
 | 
				
			||||||
                        this.tocContent += new Array(y + 1).join('</li></ul>');
 | 
					            this.tocContent += new Array(y + 1).join('</li></ul>')
 | 
				
			||||||
                        this._tempLists.length = this._tempLists.length - y;
 | 
					            this._tempLists.length = this._tempLists.length - y
 | 
				
			||||||
          } else {
 | 
					          } else {
 | 
				
			||||||
                        this._tempLists.push(this._elTitleElement);
 | 
					            this._tempLists.push(this._elTitleElement)
 | 
				
			||||||
                        if (this.ulClass)
 | 
					            if (this.ulClass) { this.tocContent += '<ul class="' + this.ulClass + '">' } else { this.tocContent += '<ul>' }
 | 
				
			||||||
                            this.tocContent += '<ul class="' + this.ulClass + '">';
 | 
					 | 
				
			||||||
                        else
 | 
					 | 
				
			||||||
                            this.tocContent += '<ul>';
 | 
					 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
                    this.tocContent += '</li>';
 | 
					          this.tocContent += '</li>'
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
        if (this._tempLists.length) {
 | 
					        if (this._tempLists.length) {
 | 
				
			||||||
                    this.tocContent += new Array(this._tempLists.length + 1).join('</li></ul>');
 | 
					          this.tocContent += new Array(this._tempLists.length + 1).join('</li></ul>')
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
                    this.tocContent += '</li>';
 | 
					          this.tocContent += '</li>'
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
        if (this.ulClass)
 | 
					    if (this.ulClass) { this.tocContent = '<ul class="' + this.ulClass + '">' + this.tocContent + '</ul>' } else { this.tocContent = '<ul>' + this.tocContent + '</ul>' }
 | 
				
			||||||
            this.tocContent = '<ul class="' + this.ulClass + '">' + this.tocContent + '</ul>';
 | 
					  }
 | 
				
			||||||
        else
 | 
					 | 
				
			||||||
            this.tocContent = '<ul>' + this.tocContent + '</ul>';
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  Toc.prototype._showToc = function () {
 | 
					  Toc.prototype._showToc = function () {
 | 
				
			||||||
        this.toc = document.createElement('div');
 | 
					    this.toc = document.createElement('div')
 | 
				
			||||||
        this.toc.innerHTML = this.tocContent;
 | 
					    this.toc.innerHTML = this.tocContent
 | 
				
			||||||
        this.toc.setAttribute('class', this.tocClass);
 | 
					    this.toc.setAttribute('class', this.tocClass)
 | 
				
			||||||
    if (!this.options.targetId) {
 | 
					    if (!this.options.targetId) {
 | 
				
			||||||
            this.el.appendChild(this.toc);
 | 
					      this.el.appendChild(this.toc)
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
            document.getElementById(this.options.targetId).appendChild(this.toc);
 | 
					      document.getElementById(this.options.targetId).appendChild(this.toc)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
        var self = this;
 | 
					    var self = this
 | 
				
			||||||
    if (this.tocTop > -1) {
 | 
					    if (this.tocTop > -1) {
 | 
				
			||||||
      window.onscroll = function () {
 | 
					      window.onscroll = function () {
 | 
				
			||||||
                var t = document.documentElement.scrollTop || document.body.scrollTop;
 | 
					        var t = document.documentElement.scrollTop || document.body.scrollTop
 | 
				
			||||||
        if (t < self.tocTop) {
 | 
					        if (t < self.tocTop) {
 | 
				
			||||||
                    self.toc.setAttribute('style', 'position:absolute;top:' + self.tocTop + 'px;');
 | 
					          self.toc.setAttribute('style', 'position:absolute;top:' + self.tocTop + 'px;')
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
                    self.toc.setAttribute('style', 'position:fixed;top:10px;');
 | 
					          self.toc.setAttribute('style', 'position:fixed;top:10px;')
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    };
 | 
					  }
 | 
				
			||||||
    window.Toc = Toc;
 | 
					  window.Toc = Toc
 | 
				
			||||||
})(window);
 | 
					})(window)
 | 
				
			||||||
 | 
				
			|||||||
@ -1,10 +1,10 @@
 | 
				
			|||||||
var baseConfig = require('./webpackBaseConfig');
 | 
					var baseConfig = require('./webpackBaseConfig')
 | 
				
			||||||
var ExtractTextPlugin = require("extract-text-webpack-plugin");
 | 
					var ExtractTextPlugin = require('extract-text-webpack-plugin')
 | 
				
			||||||
var path = require('path');
 | 
					var path = require('path')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
module.exports = [Object.assign({}, baseConfig, {
 | 
					module.exports = [Object.assign({}, baseConfig, {
 | 
				
			||||||
  plugins: baseConfig.plugins.concat([
 | 
					  plugins: baseConfig.plugins.concat([
 | 
				
			||||||
        new ExtractTextPlugin("[name].css")
 | 
					    new ExtractTextPlugin('[name].css')
 | 
				
			||||||
  ])
 | 
					  ])
 | 
				
			||||||
}), {
 | 
					}), {
 | 
				
			||||||
  entry: {
 | 
					  entry: {
 | 
				
			||||||
@ -28,6 +28,6 @@ module.exports = [Object.assign({}, baseConfig, {
 | 
				
			|||||||
    filename: '[name].js'
 | 
					    filename: '[name].js'
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  plugins: [
 | 
					  plugins: [
 | 
				
			||||||
        new ExtractTextPlugin("html.min.css")
 | 
					    new ExtractTextPlugin('html.min.css')
 | 
				
			||||||
  ]
 | 
					  ]
 | 
				
			||||||
}];
 | 
					}]
 | 
				
			||||||
 | 
				
			|||||||
@ -1,9 +1,9 @@
 | 
				
			|||||||
var baseConfig = require('./webpackBaseConfig');
 | 
					var baseConfig = require('./webpackBaseConfig')
 | 
				
			||||||
var webpack = require('webpack');
 | 
					var webpack = require('webpack')
 | 
				
			||||||
var path = require('path');
 | 
					var path = require('path')
 | 
				
			||||||
var ExtractTextPlugin = require("extract-text-webpack-plugin");
 | 
					var ExtractTextPlugin = require('extract-text-webpack-plugin')
 | 
				
			||||||
var OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
 | 
					var OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin')
 | 
				
			||||||
var ParallelUglifyPlugin = require('webpack-parallel-uglify-plugin');
 | 
					var ParallelUglifyPlugin = require('webpack-parallel-uglify-plugin')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
module.exports = [Object.assign({}, baseConfig, {
 | 
					module.exports = [Object.assign({}, baseConfig, {
 | 
				
			||||||
  plugins: baseConfig.plugins.concat([
 | 
					  plugins: baseConfig.plugins.concat([
 | 
				
			||||||
@ -21,7 +21,7 @@ module.exports = [Object.assign({}, baseConfig, {
 | 
				
			|||||||
        sourceMap: false
 | 
					        sourceMap: false
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }),
 | 
					    }),
 | 
				
			||||||
        new ExtractTextPlugin("[name].[hash].css")
 | 
					    new ExtractTextPlugin('[name].[hash].css')
 | 
				
			||||||
  ]),
 | 
					  ]),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  output: {
 | 
					  output: {
 | 
				
			||||||
@ -57,7 +57,7 @@ module.exports = [Object.assign({}, baseConfig, {
 | 
				
			|||||||
        'NODE_ENV': JSON.stringify('production')
 | 
					        'NODE_ENV': JSON.stringify('production')
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }),
 | 
					    }),
 | 
				
			||||||
        new ExtractTextPlugin("html.min.css"),
 | 
					    new ExtractTextPlugin('html.min.css'),
 | 
				
			||||||
    new OptimizeCssAssetsPlugin()
 | 
					    new OptimizeCssAssetsPlugin()
 | 
				
			||||||
  ]
 | 
					  ]
 | 
				
			||||||
}];
 | 
					}]
 | 
				
			||||||
 | 
				
			|||||||
@ -1,24 +1,24 @@
 | 
				
			|||||||
var webpack = require('webpack');
 | 
					var webpack = require('webpack')
 | 
				
			||||||
var path = require('path');
 | 
					var path = require('path')
 | 
				
			||||||
var ExtractTextPlugin = require("extract-text-webpack-plugin");
 | 
					var ExtractTextPlugin = require('extract-text-webpack-plugin')
 | 
				
			||||||
var HtmlWebpackPlugin = require('html-webpack-plugin');
 | 
					var HtmlWebpackPlugin = require('html-webpack-plugin')
 | 
				
			||||||
var CopyWebpackPlugin = require('copy-webpack-plugin');
 | 
					var CopyWebpackPlugin = require('copy-webpack-plugin')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
module.exports = {
 | 
					module.exports = {
 | 
				
			||||||
  plugins: [
 | 
					  plugins: [
 | 
				
			||||||
    new webpack.ProvidePlugin({
 | 
					    new webpack.ProvidePlugin({
 | 
				
			||||||
            Visibility: "visibilityjs",
 | 
					      Visibility: 'visibilityjs',
 | 
				
			||||||
            Cookies: "js-cookie",
 | 
					      Cookies: 'js-cookie',
 | 
				
			||||||
            key: "keymaster",
 | 
					      key: 'keymaster',
 | 
				
			||||||
            $: "jquery",
 | 
					      $: 'jquery',
 | 
				
			||||||
            jQuery: "jquery",
 | 
					      jQuery: 'jquery',
 | 
				
			||||||
            "window.jQuery": "jquery",
 | 
					      'window.jQuery': 'jquery',
 | 
				
			||||||
            "moment": "moment",
 | 
					      'moment': 'moment',
 | 
				
			||||||
            "Handlebars": "handlebars"
 | 
					      'Handlebars': 'handlebars'
 | 
				
			||||||
    }),
 | 
					    }),
 | 
				
			||||||
    new webpack.optimize.OccurrenceOrderPlugin(true),
 | 
					    new webpack.optimize.OccurrenceOrderPlugin(true),
 | 
				
			||||||
    new webpack.optimize.CommonsChunkPlugin({
 | 
					    new webpack.optimize.CommonsChunkPlugin({
 | 
				
			||||||
            names: ["cover", "index", "pretty", "slide", "vendor"],
 | 
					      names: ['cover', 'index', 'pretty', 'slide', 'vendor'],
 | 
				
			||||||
      children: true,
 | 
					      children: true,
 | 
				
			||||||
      async: true,
 | 
					      async: true,
 | 
				
			||||||
      filename: '[name].js',
 | 
					      filename: '[name].js',
 | 
				
			||||||
@ -159,57 +159,56 @@ module.exports = {
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
    ])
 | 
					    ])
 | 
				
			||||||
  ],
 | 
					  ],
 | 
				
			||||||
 | 
					 | 
				
			||||||
  entry: {
 | 
					  entry: {
 | 
				
			||||||
    font: path.join(__dirname, 'public/css/google-font.css'),
 | 
					    font: path.join(__dirname, 'public/css/google-font.css'),
 | 
				
			||||||
        "font-pack": path.join(__dirname, 'public/css/font.css'),
 | 
					    'font-pack': path.join(__dirname, 'public/css/font.css'),
 | 
				
			||||||
    common: [
 | 
					    common: [
 | 
				
			||||||
            "expose?jQuery!expose?$!jquery",
 | 
					      'expose?jQuery!expose?$!jquery',
 | 
				
			||||||
            "velocity-animate",
 | 
					      'velocity-animate',
 | 
				
			||||||
            "imports?$=jquery!jquery-mousewheel",
 | 
					      'imports?$=jquery!jquery-mousewheel',
 | 
				
			||||||
            "bootstrap"
 | 
					      'bootstrap'
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
    cover: [
 | 
					    cover: [
 | 
				
			||||||
            "babel-polyfill",
 | 
					      'babel-polyfill',
 | 
				
			||||||
      path.join(__dirname, 'public/js/cover.js')
 | 
					      path.join(__dirname, 'public/js/cover.js')
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
        "cover-styles-pack": [
 | 
					    'cover-styles-pack': [
 | 
				
			||||||
      path.join(__dirname, 'node_modules/bootstrap/dist/css/bootstrap.min.css'),
 | 
					      path.join(__dirname, 'node_modules/bootstrap/dist/css/bootstrap.min.css'),
 | 
				
			||||||
      path.join(__dirname, 'node_modules/font-awesome/css/font-awesome.min.css'),
 | 
					      path.join(__dirname, 'node_modules/font-awesome/css/font-awesome.min.css'),
 | 
				
			||||||
      path.join(__dirname, 'public/css/bootstrap-social.css'),
 | 
					      path.join(__dirname, 'public/css/bootstrap-social.css'),
 | 
				
			||||||
      path.join(__dirname, 'node_modules/select2/select2.css'),
 | 
					      path.join(__dirname, 'node_modules/select2/select2.css'),
 | 
				
			||||||
            path.join(__dirname, 'node_modules/select2/select2-bootstrap.css'),
 | 
					      path.join(__dirname, 'node_modules/select2/select2-bootstrap.css')
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
        "cover-pack": [
 | 
					    'cover-pack': [
 | 
				
			||||||
            "babel-polyfill",
 | 
					      'babel-polyfill',
 | 
				
			||||||
            "bootstrap-validator",
 | 
					      'bootstrap-validator',
 | 
				
			||||||
            "script!listPagnation",
 | 
					      'script!listPagnation',
 | 
				
			||||||
            "expose?select2!select2",
 | 
					      'expose?select2!select2',
 | 
				
			||||||
            "expose?moment!moment",
 | 
					      'expose?moment!moment',
 | 
				
			||||||
            "script!js-url",
 | 
					      'script!js-url',
 | 
				
			||||||
      path.join(__dirname, 'public/js/cover.js')
 | 
					      path.join(__dirname, 'public/js/cover.js')
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
    index: [
 | 
					    index: [
 | 
				
			||||||
            "babel-polyfill",
 | 
					      'babel-polyfill',
 | 
				
			||||||
            "script!jquery-ui-resizable",
 | 
					      'script!jquery-ui-resizable',
 | 
				
			||||||
            "script!js-url",
 | 
					      'script!js-url',
 | 
				
			||||||
            "expose?filterXSS!xss",
 | 
					      'expose?filterXSS!xss',
 | 
				
			||||||
            "script!Idle.Js",
 | 
					      'script!Idle.Js',
 | 
				
			||||||
            "expose?LZString!lz-string",
 | 
					      'expose?LZString!lz-string',
 | 
				
			||||||
            "script!codemirror",
 | 
					      'script!codemirror',
 | 
				
			||||||
            "script!inlineAttachment",
 | 
					      'script!inlineAttachment',
 | 
				
			||||||
            "script!jqueryTextcomplete",
 | 
					      'script!jqueryTextcomplete',
 | 
				
			||||||
            "script!codemirrorSpellChecker",
 | 
					      'script!codemirrorSpellChecker',
 | 
				
			||||||
            "script!codemirrorInlineAttachment",
 | 
					      'script!codemirrorInlineAttachment',
 | 
				
			||||||
            "script!ot",
 | 
					      'script!ot',
 | 
				
			||||||
            "flowchart.js",
 | 
					      'flowchart.js',
 | 
				
			||||||
            "js-sequence-diagrams",
 | 
					      'js-sequence-diagrams',
 | 
				
			||||||
            "expose?RevealMarkdown!reveal-markdown",
 | 
					      'expose?RevealMarkdown!reveal-markdown',
 | 
				
			||||||
      path.join(__dirname, 'public/js/google-drive-upload.js'),
 | 
					      path.join(__dirname, 'public/js/google-drive-upload.js'),
 | 
				
			||||||
      path.join(__dirname, 'public/js/google-drive-picker.js'),
 | 
					      path.join(__dirname, 'public/js/google-drive-picker.js'),
 | 
				
			||||||
      path.join(__dirname, 'public/js/index.js')
 | 
					      path.join(__dirname, 'public/js/index.js')
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
        "index-styles": [
 | 
					    'index-styles': [
 | 
				
			||||||
      path.join(__dirname, 'public/vendor/jquery-ui/jquery-ui.min.css'),
 | 
					      path.join(__dirname, 'public/vendor/jquery-ui/jquery-ui.min.css'),
 | 
				
			||||||
      path.join(__dirname, 'public/vendor/codemirror-spell-checker/spell-checker.min.css'),
 | 
					      path.join(__dirname, 'public/vendor/codemirror-spell-checker/spell-checker.min.css'),
 | 
				
			||||||
      path.join(__dirname, 'node_modules/codemirror/lib/codemirror.css'),
 | 
					      path.join(__dirname, 'node_modules/codemirror/lib/codemirror.css'),
 | 
				
			||||||
@ -228,120 +227,120 @@ module.exports = {
 | 
				
			|||||||
      path.join(__dirname, 'public/css/markdown.css'),
 | 
					      path.join(__dirname, 'public/css/markdown.css'),
 | 
				
			||||||
      path.join(__dirname, 'public/css/slide-preview.css')
 | 
					      path.join(__dirname, 'public/css/slide-preview.css')
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
        "index-styles-pack": [
 | 
					    'index-styles-pack': [
 | 
				
			||||||
      path.join(__dirname, 'node_modules/bootstrap/dist/css/bootstrap.min.css'),
 | 
					      path.join(__dirname, 'node_modules/bootstrap/dist/css/bootstrap.min.css'),
 | 
				
			||||||
      path.join(__dirname, 'node_modules/font-awesome/css/font-awesome.min.css'),
 | 
					      path.join(__dirname, 'node_modules/font-awesome/css/font-awesome.min.css'),
 | 
				
			||||||
      path.join(__dirname, 'public/css/bootstrap-social.css'),
 | 
					      path.join(__dirname, 'public/css/bootstrap-social.css'),
 | 
				
			||||||
      path.join(__dirname, 'node_modules/ionicons/css/ionicons.min.css'),
 | 
					      path.join(__dirname, 'node_modules/ionicons/css/ionicons.min.css'),
 | 
				
			||||||
      path.join(__dirname, 'node_modules/octicons/octicons/octicons.css')
 | 
					      path.join(__dirname, 'node_modules/octicons/octicons/octicons.css')
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
        "index-pack": [
 | 
					    'index-pack': [
 | 
				
			||||||
            "babel-polyfill",
 | 
					      'babel-polyfill',
 | 
				
			||||||
            "expose?Spinner!spin.js",
 | 
					      'expose?Spinner!spin.js',
 | 
				
			||||||
            "script!jquery-ui-resizable",
 | 
					      'script!jquery-ui-resizable',
 | 
				
			||||||
            "bootstrap-validator",
 | 
					      'bootstrap-validator',
 | 
				
			||||||
            "expose?jsyaml!js-yaml",
 | 
					      'expose?jsyaml!js-yaml',
 | 
				
			||||||
            "script!mermaid",
 | 
					      'script!mermaid',
 | 
				
			||||||
            "expose?moment!moment",
 | 
					      'expose?moment!moment',
 | 
				
			||||||
            "script!js-url",
 | 
					      'script!js-url',
 | 
				
			||||||
            "script!handlebars",
 | 
					      'script!handlebars',
 | 
				
			||||||
            "expose?hljs!highlight.js",
 | 
					      'expose?hljs!highlight.js',
 | 
				
			||||||
            "expose?emojify!emojify.js",
 | 
					      'expose?emojify!emojify.js',
 | 
				
			||||||
            "expose?filterXSS!xss",
 | 
					      'expose?filterXSS!xss',
 | 
				
			||||||
            "script!Idle.Js",
 | 
					      'script!Idle.Js',
 | 
				
			||||||
            "script!gist-embed",
 | 
					      'script!gist-embed',
 | 
				
			||||||
            "expose?LZString!lz-string",
 | 
					      'expose?LZString!lz-string',
 | 
				
			||||||
            "script!codemirror",
 | 
					      'script!codemirror',
 | 
				
			||||||
            "script!inlineAttachment",
 | 
					      'script!inlineAttachment',
 | 
				
			||||||
            "script!jqueryTextcomplete",
 | 
					      'script!jqueryTextcomplete',
 | 
				
			||||||
            "script!codemirrorSpellChecker",
 | 
					      'script!codemirrorSpellChecker',
 | 
				
			||||||
            "script!codemirrorInlineAttachment",
 | 
					      'script!codemirrorInlineAttachment',
 | 
				
			||||||
            "script!ot",
 | 
					      'script!ot',
 | 
				
			||||||
            "flowchart.js",
 | 
					      'flowchart.js',
 | 
				
			||||||
            "js-sequence-diagrams",
 | 
					      'js-sequence-diagrams',
 | 
				
			||||||
            "expose?Viz!viz.js",
 | 
					      'expose?Viz!viz.js',
 | 
				
			||||||
            "expose?io!socket.io-client",
 | 
					      'expose?io!socket.io-client',
 | 
				
			||||||
            "expose?RevealMarkdown!reveal-markdown",
 | 
					      'expose?RevealMarkdown!reveal-markdown',
 | 
				
			||||||
      path.join(__dirname, 'public/js/google-drive-upload.js'),
 | 
					      path.join(__dirname, 'public/js/google-drive-upload.js'),
 | 
				
			||||||
      path.join(__dirname, 'public/js/google-drive-picker.js'),
 | 
					      path.join(__dirname, 'public/js/google-drive-picker.js'),
 | 
				
			||||||
      path.join(__dirname, 'public/js/index.js')
 | 
					      path.join(__dirname, 'public/js/index.js')
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
    pretty: [
 | 
					    pretty: [
 | 
				
			||||||
            "babel-polyfill",
 | 
					      'babel-polyfill',
 | 
				
			||||||
            "expose?filterXSS!xss",
 | 
					      'expose?filterXSS!xss',
 | 
				
			||||||
            "flowchart.js",
 | 
					      'flowchart.js',
 | 
				
			||||||
            "js-sequence-diagrams",
 | 
					      'js-sequence-diagrams',
 | 
				
			||||||
            "expose?RevealMarkdown!reveal-markdown",
 | 
					      'expose?RevealMarkdown!reveal-markdown',
 | 
				
			||||||
      path.join(__dirname, 'public/js/pretty.js')
 | 
					      path.join(__dirname, 'public/js/pretty.js')
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
        "pretty-styles": [
 | 
					    'pretty-styles': [
 | 
				
			||||||
      path.join(__dirname, 'public/css/github-extract.css'),
 | 
					      path.join(__dirname, 'public/css/github-extract.css'),
 | 
				
			||||||
      path.join(__dirname, 'public/css/mermaid.css'),
 | 
					      path.join(__dirname, 'public/css/mermaid.css'),
 | 
				
			||||||
      path.join(__dirname, 'public/css/markdown.css'),
 | 
					      path.join(__dirname, 'public/css/markdown.css'),
 | 
				
			||||||
      path.join(__dirname, 'public/css/slide-preview.css')
 | 
					      path.join(__dirname, 'public/css/slide-preview.css')
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
        "pretty-styles-pack": [
 | 
					    'pretty-styles-pack': [
 | 
				
			||||||
      path.join(__dirname, 'node_modules/bootstrap/dist/css/bootstrap.min.css'),
 | 
					      path.join(__dirname, 'node_modules/bootstrap/dist/css/bootstrap.min.css'),
 | 
				
			||||||
      path.join(__dirname, 'node_modules/font-awesome/css/font-awesome.min.css'),
 | 
					      path.join(__dirname, 'node_modules/font-awesome/css/font-awesome.min.css'),
 | 
				
			||||||
      path.join(__dirname, 'node_modules/ionicons/css/ionicons.min.css'),
 | 
					      path.join(__dirname, 'node_modules/ionicons/css/ionicons.min.css'),
 | 
				
			||||||
      path.join(__dirname, 'node_modules/octicons/octicons/octicons.css')
 | 
					      path.join(__dirname, 'node_modules/octicons/octicons/octicons.css')
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
        "pretty-pack": [
 | 
					    'pretty-pack': [
 | 
				
			||||||
            "babel-polyfill",
 | 
					      'babel-polyfill',
 | 
				
			||||||
            "expose?jsyaml!js-yaml",
 | 
					      'expose?jsyaml!js-yaml',
 | 
				
			||||||
            "script!mermaid",
 | 
					      'script!mermaid',
 | 
				
			||||||
            "expose?moment!moment",
 | 
					      'expose?moment!moment',
 | 
				
			||||||
            "script!handlebars",
 | 
					      'script!handlebars',
 | 
				
			||||||
            "expose?hljs!highlight.js",
 | 
					      'expose?hljs!highlight.js',
 | 
				
			||||||
            "expose?emojify!emojify.js",
 | 
					      'expose?emojify!emojify.js',
 | 
				
			||||||
            "expose?filterXSS!xss",
 | 
					      'expose?filterXSS!xss',
 | 
				
			||||||
            "script!gist-embed",
 | 
					      'script!gist-embed',
 | 
				
			||||||
            "flowchart.js",
 | 
					      'flowchart.js',
 | 
				
			||||||
            "js-sequence-diagrams",
 | 
					      'js-sequence-diagrams',
 | 
				
			||||||
            "expose?Viz!viz.js",
 | 
					      'expose?Viz!viz.js',
 | 
				
			||||||
            "expose?RevealMarkdown!reveal-markdown",
 | 
					      'expose?RevealMarkdown!reveal-markdown',
 | 
				
			||||||
      path.join(__dirname, 'public/js/pretty.js')
 | 
					      path.join(__dirname, 'public/js/pretty.js')
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
    slide: [
 | 
					    slide: [
 | 
				
			||||||
            "babel-polyfill",
 | 
					      'babel-polyfill',
 | 
				
			||||||
            "bootstrap-tooltip",
 | 
					      'bootstrap-tooltip',
 | 
				
			||||||
            "expose?filterXSS!xss",
 | 
					      'expose?filterXSS!xss',
 | 
				
			||||||
            "flowchart.js",
 | 
					      'flowchart.js',
 | 
				
			||||||
            "js-sequence-diagrams",
 | 
					      'js-sequence-diagrams',
 | 
				
			||||||
            "expose?RevealMarkdown!reveal-markdown",
 | 
					      'expose?RevealMarkdown!reveal-markdown',
 | 
				
			||||||
      path.join(__dirname, 'public/js/slide.js')
 | 
					      path.join(__dirname, 'public/js/slide.js')
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
        "slide-styles": [
 | 
					    'slide-styles': [
 | 
				
			||||||
      path.join(__dirname, 'public/vendor/bootstrap/tooltip.min.css'),
 | 
					      path.join(__dirname, 'public/vendor/bootstrap/tooltip.min.css'),
 | 
				
			||||||
      path.join(__dirname, 'public/css/github-extract.css'),
 | 
					      path.join(__dirname, 'public/css/github-extract.css'),
 | 
				
			||||||
      path.join(__dirname, 'public/css/mermaid.css'),
 | 
					      path.join(__dirname, 'public/css/mermaid.css'),
 | 
				
			||||||
      path.join(__dirname, 'public/css/markdown.css')
 | 
					      path.join(__dirname, 'public/css/markdown.css')
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
        "slide-styles-pack": [
 | 
					    'slide-styles-pack': [
 | 
				
			||||||
      path.join(__dirname, 'node_modules/font-awesome/css/font-awesome.min.css'),
 | 
					      path.join(__dirname, 'node_modules/font-awesome/css/font-awesome.min.css'),
 | 
				
			||||||
      path.join(__dirname, 'node_modules/ionicons/css/ionicons.min.css'),
 | 
					      path.join(__dirname, 'node_modules/ionicons/css/ionicons.min.css'),
 | 
				
			||||||
      path.join(__dirname, 'node_modules/octicons/octicons/octicons.css')
 | 
					      path.join(__dirname, 'node_modules/octicons/octicons/octicons.css')
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
        "slide-pack": [
 | 
					    'slide-pack': [
 | 
				
			||||||
            "babel-polyfill",
 | 
					      'babel-polyfill',
 | 
				
			||||||
            "expose?jQuery!expose?$!jquery",
 | 
					      'expose?jQuery!expose?$!jquery',
 | 
				
			||||||
            "velocity-animate",
 | 
					      'velocity-animate',
 | 
				
			||||||
            "imports?$=jquery!jquery-mousewheel",
 | 
					      'imports?$=jquery!jquery-mousewheel',
 | 
				
			||||||
            "bootstrap-tooltip",
 | 
					      'bootstrap-tooltip',
 | 
				
			||||||
            "expose?jsyaml!js-yaml",
 | 
					      'expose?jsyaml!js-yaml',
 | 
				
			||||||
            "script!mermaid",
 | 
					      'script!mermaid',
 | 
				
			||||||
            "expose?moment!moment",
 | 
					      'expose?moment!moment',
 | 
				
			||||||
            "script!handlebars",
 | 
					      'script!handlebars',
 | 
				
			||||||
            "expose?hljs!highlight.js",
 | 
					      'expose?hljs!highlight.js',
 | 
				
			||||||
            "expose?emojify!emojify.js",
 | 
					      'expose?emojify!emojify.js',
 | 
				
			||||||
            "expose?filterXSS!xss",
 | 
					      'expose?filterXSS!xss',
 | 
				
			||||||
            "script!gist-embed",
 | 
					      'script!gist-embed',
 | 
				
			||||||
            "flowchart.js",
 | 
					      'flowchart.js',
 | 
				
			||||||
            "js-sequence-diagrams",
 | 
					      'js-sequence-diagrams',
 | 
				
			||||||
            "expose?Viz!viz.js",
 | 
					      'expose?Viz!viz.js',
 | 
				
			||||||
            "headjs",
 | 
					      'headjs',
 | 
				
			||||||
            "expose?Reveal!reveal.js",
 | 
					      'expose?Reveal!reveal.js',
 | 
				
			||||||
            "expose?RevealMarkdown!reveal-markdown",
 | 
					      'expose?RevealMarkdown!reveal-markdown',
 | 
				
			||||||
      path.join(__dirname, 'public/js/slide.js')
 | 
					      path.join(__dirname, 'public/js/slide.js')
 | 
				
			||||||
    ]
 | 
					    ]
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
@ -358,7 +357,7 @@ module.exports = {
 | 
				
			|||||||
      path.resolve(__dirname, 'src'),
 | 
					      path.resolve(__dirname, 'src'),
 | 
				
			||||||
      path.resolve(__dirname, 'node_modules')
 | 
					      path.resolve(__dirname, 'node_modules')
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
        extensions: ["", ".js"],
 | 
					    extensions: ['', '.js'],
 | 
				
			||||||
    alias: {
 | 
					    alias: {
 | 
				
			||||||
      codemirror: path.join(__dirname, 'node_modules/codemirror/codemirror.min.js'),
 | 
					      codemirror: path.join(__dirname, 'node_modules/codemirror/codemirror.min.js'),
 | 
				
			||||||
      inlineAttachment: path.join(__dirname, 'public/vendor/inlineAttachment/inline-attachment.js'),
 | 
					      inlineAttachment: path.join(__dirname, 'public/vendor/inlineAttachment/inline-attachment.js'),
 | 
				
			||||||
@ -369,23 +368,23 @@ module.exports = {
 | 
				
			|||||||
      listPagnation: path.join(__dirname, 'node_modules/list.pagination.js/dist/list.pagination.min.js'),
 | 
					      listPagnation: path.join(__dirname, 'node_modules/list.pagination.js/dist/list.pagination.min.js'),
 | 
				
			||||||
      mermaid: path.join(__dirname, 'node_modules/mermaid/dist/mermaid.min.js'),
 | 
					      mermaid: path.join(__dirname, 'node_modules/mermaid/dist/mermaid.min.js'),
 | 
				
			||||||
      handlebars: path.join(__dirname, 'node_modules/handlebars/dist/handlebars.min.js'),
 | 
					      handlebars: path.join(__dirname, 'node_modules/handlebars/dist/handlebars.min.js'),
 | 
				
			||||||
            "jquery-ui-resizable": path.join(__dirname, 'public/vendor/jquery-ui/jquery-ui.min.js'),
 | 
					      'jquery-ui-resizable': path.join(__dirname, 'public/vendor/jquery-ui/jquery-ui.min.js'),
 | 
				
			||||||
            "gist-embed": path.join(__dirname, 'node_modules/gist-embed/gist-embed.min.js'),
 | 
					      'gist-embed': path.join(__dirname, 'node_modules/gist-embed/gist-embed.min.js'),
 | 
				
			||||||
            "bootstrap-tooltip": path.join(__dirname, 'public/vendor/bootstrap/tooltip.min.js'),
 | 
					      'bootstrap-tooltip': path.join(__dirname, 'public/vendor/bootstrap/tooltip.min.js'),
 | 
				
			||||||
            "headjs": path.join(__dirname, 'node_modules/reveal.js/lib/js/head.min.js'),
 | 
					      'headjs': path.join(__dirname, 'node_modules/reveal.js/lib/js/head.min.js'),
 | 
				
			||||||
            "reveal-markdown": path.join(__dirname, 'public/js/reveal-markdown.js')
 | 
					      'reveal-markdown': path.join(__dirname, 'public/js/reveal-markdown.js')
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  externals: {
 | 
					  externals: {
 | 
				
			||||||
        "viz.js": "Viz",
 | 
					    'viz.js': 'Viz',
 | 
				
			||||||
        "socket.io-client": "io",
 | 
					    'socket.io-client': 'io',
 | 
				
			||||||
        "lodash": "_",
 | 
					    'lodash': '_',
 | 
				
			||||||
        "jquery": "$",
 | 
					    'jquery': '$',
 | 
				
			||||||
        "moment": "moment",
 | 
					    'moment': 'moment',
 | 
				
			||||||
        "handlebars": "Handlebars",
 | 
					    'handlebars': 'Handlebars',
 | 
				
			||||||
        "highlight.js": "hljs",
 | 
					    'highlight.js': 'hljs',
 | 
				
			||||||
        "select2": "select2"
 | 
					    'select2': 'select2'
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  module: {
 | 
					  module: {
 | 
				
			||||||
@ -406,31 +405,30 @@ module.exports = {
 | 
				
			|||||||
      test: /\.less$/,
 | 
					      test: /\.less$/,
 | 
				
			||||||
      loader: ExtractTextPlugin.extract('style-loader', 'less-loader')
 | 
					      loader: ExtractTextPlugin.extract('style-loader', 'less-loader')
 | 
				
			||||||
    }, {
 | 
					    }, {
 | 
				
			||||||
            test: require.resolve("js-sequence-diagrams"),
 | 
					      test: require.resolve('js-sequence-diagrams'),
 | 
				
			||||||
            loader: "imports?Raphael=raphael"
 | 
					      loader: 'imports?Raphael=raphael'
 | 
				
			||||||
    }, {
 | 
					    }, {
 | 
				
			||||||
      test: /\.eot(\?v=\d+\.\d+\.\d+)?$/,
 | 
					      test: /\.eot(\?v=\d+\.\d+\.\d+)?$/,
 | 
				
			||||||
            loader: "file"
 | 
					      loader: 'file'
 | 
				
			||||||
    }, {
 | 
					    }, {
 | 
				
			||||||
      test: /\.(woff|woff2)(\?v=\d+\.\d+\.\d+)?$/,
 | 
					      test: /\.(woff|woff2)(\?v=\d+\.\d+\.\d+)?$/,
 | 
				
			||||||
            loader: "url?prefix=font/&limit=5000"
 | 
					      loader: 'url?prefix=font/&limit=5000'
 | 
				
			||||||
    }, {
 | 
					    }, {
 | 
				
			||||||
      test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/,
 | 
					      test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/,
 | 
				
			||||||
            loader: "url?limit=10000&mimetype=application/octet-stream"
 | 
					      loader: 'url?limit=10000&mimetype=application/octet-stream'
 | 
				
			||||||
    }, {
 | 
					    }, {
 | 
				
			||||||
      test: /\.svg(\?v=\d+\.\d+\.\d+)?$/,
 | 
					      test: /\.svg(\?v=\d+\.\d+\.\d+)?$/,
 | 
				
			||||||
            loader: "url?limit=10000&mimetype=image/svg+xml"
 | 
					      loader: 'url?limit=10000&mimetype=image/svg+xml'
 | 
				
			||||||
    }, {
 | 
					    }, {
 | 
				
			||||||
      test: /\.png(\?v=\d+\.\d+\.\d+)?$/,
 | 
					      test: /\.png(\?v=\d+\.\d+\.\d+)?$/,
 | 
				
			||||||
            loader: "url?limit=10000&mimetype=image/png"
 | 
					      loader: 'url?limit=10000&mimetype=image/png'
 | 
				
			||||||
    }, {
 | 
					    }, {
 | 
				
			||||||
      test: /\.gif(\?v=\d+\.\d+\.\d+)?$/,
 | 
					      test: /\.gif(\?v=\d+\.\d+\.\d+)?$/,
 | 
				
			||||||
            loader: "url?limit=10000&mimetype=image/gif"
 | 
					      loader: 'url?limit=10000&mimetype=image/gif'
 | 
				
			||||||
    }]
 | 
					    }]
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
 | 
					 | 
				
			||||||
  node: {
 | 
					  node: {
 | 
				
			||||||
        fs: "empty"
 | 
					    fs: 'empty'
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  quiet: false,
 | 
					  quiet: false,
 | 
				
			||||||
@ -438,4 +436,4 @@ module.exports = {
 | 
				
			|||||||
  stats: {
 | 
					  stats: {
 | 
				
			||||||
    assets: false
 | 
					    assets: false
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
};
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user