diff --git a/package.json b/package.json
index 03333fb..6b313be 100644
--- a/package.json
+++ b/package.json
@@ -37,7 +37,6 @@
     "imgur": "git+https://github.com/hackmdio/node-imgur.git",
     "jquery": "^3.1.1",
     "jquery-mousewheel": "^3.1.13",
-    "jquery-scrollspy": "github:softwarespot/jquery-scrollspy",
     "jquery-textcomplete": "^1.7.3",
     "jquery-ui": "^1.12.1",
     "js-cookie": "^2.1.3",
diff --git a/public/js/common.js b/public/js/common.js
index 19455f3..b8ff375 100644
--- a/public/js/common.js
+++ b/public/js/common.js
@@ -8,7 +8,7 @@ var DROPBOX_APP_KEY = config.DROPBOX_APP_KEY;
 
 //common
 var port = window.location.port;
-var serverurl = window.location.protocol + '//' + (domain ? domain : window.location.hostname) + (port ? ':' + port : '') + (urlpath ? '/' + urlpath : '');
+window.serverurl = window.location.protocol + '//' + (domain ? domain : window.location.hostname) + (port ? ':' + port : '') + (urlpath ? '/' + urlpath : '');
 var noteid = urlpath ? window.location.pathname.slice(urlpath.length + 1, window.location.pathname.length).split('/')[1] : window.location.pathname.split('/')[1];
 var noteurl = serverurl + '/' + noteid;
 
@@ -93,29 +93,28 @@ function checkIfAuth(yesCallback, noCallback) {
 }
 
 module.exports = {
-  domain: domain,
-  urlpath: urlpath,
-  debug: debug,
-  GOOGLE_API_KEY: GOOGLE_API_KEY,
-  GOOGLE_CLIENT_ID: GOOGLE_CLIENT_ID,
-  DROPBOX_APP_KEY: DROPBOX_APP_KEY,
-  port: port,
-  serverurl: serverurl,
-  noteid: noteid,
-  noteurl: noteurl,
-  version: version,
-  checkAuth: checkAuth,
-  profile: profile,
-  lastLoginState: lastLoginState,
-  lastUserId: lastUserId,
-  loginStateChangeEvent: loginStateChangeEvent,
+    domain: domain,
+    urlpath: urlpath,
+    debug: debug,
+    GOOGLE_API_KEY: GOOGLE_API_KEY,
+    GOOGLE_CLIENT_ID: GOOGLE_CLIENT_ID,
+    DROPBOX_APP_KEY: DROPBOX_APP_KEY,
+    port: port,
+    noteid: noteid,
+    noteurl: noteurl,
+    version: version,
+    checkAuth: checkAuth,
+    profile: profile,
+    lastLoginState: lastLoginState,
+    lastUserId: lastUserId,
+    loginStateChangeEvent: loginStateChangeEvent,
 
-  /* export functions */
-  resetCheckAuth: resetCheckAuth,
-  setLoginState: setLoginState,
-  checkLoginStateChanged: checkLoginStateChanged,
-  getLoginState: getLoginState,
-  getUserId: getUserId,
-  clearLoginState: clearLoginState,
-  checkIfAuth: checkIfAuth
+    /* export functions */
+    resetCheckAuth: resetCheckAuth,
+    setLoginState: setLoginState,
+    checkLoginStateChanged: checkLoginStateChanged,
+    getLoginState: getLoginState,
+    getUserId: getUserId,
+    clearLoginState: clearLoginState,
+    checkIfAuth: checkIfAuth
 };
diff --git a/public/js/cover.js b/public/js/cover.js
index 30a8e5c..3dd4de0 100644
--- a/public/js/cover.js
+++ b/public/js/cover.js
@@ -1,7 +1,6 @@
 var common = require('./common');
 var checkIfAuth = common.checkIfAuth;
 var urlpath = common.urlpath;
-var serverurl = common.serverurl;
 var resetCheckAuth = common.resetCheckAuth;
 var getLoginState = common.getLoginState;
 var clearLoginState = common.clearLoginState;
@@ -17,6 +16,7 @@ var postHistoryToServer = historyModule.postHistoryToServer;
 var deleteServerHistory = historyModule.deleteServerHistory;
 var parseServerToHistory = historyModule.parseServerToHistory;
 var saveStorageHistoryToServer = historyModule.saveStorageHistoryToServer;
+var clearDuplicatedHistory = historyModule.clearDuplicatedHistory;
 
 var saveAs = require('file-saver').saveAs;
 var List = require('list.js');
diff --git a/public/js/extra.js b/public/js/extra.js
index 24d53fe..7cae068 100644
--- a/public/js/extra.js
+++ b/public/js/extra.js
@@ -5,9 +5,9 @@ var saveAs = require('file-saver').saveAs;
 require('../vendor/md-toc');
 
 //auto update last change
-var createtime = null;
-var lastchangetime = null;
-var lastchangeui = {
+window.createtime = null;
+window.lastchangetime = null;
+window.lastchangeui = {
     status: $(".ui-status-lastchange"),
     time: $(".ui-lastchange"),
     user: $(".ui-lastchangeuser"),
@@ -30,8 +30,8 @@ function updateLastChange() {
 }
 setInterval(updateLastChange, 60000);
 
-var lastchangeuser = null;
-var lastchangeuserprofile = null;
+window.lastchangeuser = null;
+window.lastchangeuserprofile = null;
 function updateLastChangeUser() {
     if (lastchangeui) {
       if (lastchangeuser && lastchangeuserprofile) {
@@ -544,7 +544,6 @@ function exportToRawHTML(view) {
 }
 
 var common = require('./common.js');
-var serverurl = common.serverurl;
 //extract markdown body to html and compile to template
 function exportToHTML(view) {
     var title = renderTitle(ui.area.markdown);
@@ -1070,11 +1069,7 @@ md.use(pdfPlugin);
 
 module.exports = {
   md: md,
-  createtime: createtime,
-  lastchangetime: lastchangetime,
   updateLastChange: updateLastChange,
-  lastchangeui: lastchangeui,
-  lastchangeuser: lastchangeuser,
   postProcess: postProcess,
   finishView: finishView,
   autoLinkify: autoLinkify,
@@ -1087,5 +1082,8 @@ module.exports = {
   scrollToHash: scrollToHash,
   owner: owner,
   updateLastChangeUser: updateLastChangeUser,
-  updateOwner: updateOwner
+  updateOwner: updateOwner,
+  parseMeta: parseMeta,
+  exportToHTML: exportToHTML,
+  exportToRawHTML: exportToRawHTML
 };
diff --git a/public/js/history.js b/public/js/history.js
index d5082dd..d924eec 100644
--- a/public/js/history.js
+++ b/public/js/history.js
@@ -3,14 +3,13 @@ var store = require('store');
 var common = require('./common');
 var checkIfAuth = common.checkIfAuth;
 var urlpath = common.urlpath;
-var serverurl = common.serverurl;
 var getLoginState = common.getLoginState;
 
 var extra = require('./extra');
 var renderFilename = extra.renderFilename;
 var md = extra.md;
 
-var migrateHistoryFromTempCallback = null;
+window.migrateHistoryFromTempCallback = null;
 
 migrateHistoryFromTemp();
 
@@ -417,5 +416,6 @@ module.exports = {
     postHistoryToServer: postHistoryToServer,
     deleteServerHistory: deleteServerHistory,
     parseServerToHistory: parseServerToHistory,
-    saveStorageHistoryToServer: saveStorageHistoryToServer
+    saveStorageHistoryToServer: saveStorageHistoryToServer,
+    clearDuplicatedHistory: clearDuplicatedHistory
 }
diff --git a/public/js/index.js b/public/js/index.js
index 20a76ff..5246f94 100644
--- a/public/js/index.js
+++ b/public/js/index.js
@@ -21,7 +21,6 @@ var urlpath = common.urlpath;
 var noteid = common.noteid;
 var debug = common.debug;
 var version = common.version;
-var serverurl = common.serverurl;
 var GOOGLE_API_KEY = common.GOOGLE_API_KEY;
 var GOOGLE_CLIENT_ID = common.GOOGLE_CLIENT_ID;
 var DROPBOX_APP_KEY = common.DROPBOX_APP_KEY;
@@ -38,15 +37,12 @@ var syncScrollToView = syncScroll.syncScrollToView;
 require('./pretty');
 var extra = require('./extra');
 var md = extra.md;
-var createtime = extra.createtime;
 var updateLastChange = extra.updateLastChange;
 var postProcess = extra.postProcess;
 var finishView = extra.finishView;
-var lastchangetime = extra.lastchangetime;
 var autoLinkify = extra.autoLinkify;
 var generateToc = extra.generateToc;
 var smoothHashScroll = extra.smoothHashScroll;
-var lastchangeuser = extra.lastchangeuser;
 var deduplicatedHeaderId = extra.deduplicatedHeaderId;
 var renderTOC = extra.renderTOC;
 var renderTitle = extra.renderTitle;
@@ -55,6 +51,9 @@ var scrollToHash = extra.scrollToHash;
 var owner = extra.owner;
 var updateLastChangeUser = extra.updateLastChangeUser;
 var updateOwner = extra.updateOwner;
+var parseMeta = extra.parseMeta;
+var exportToHTML = extra.exportToHTML;
+var exportToRawHTML = extra.exportToRawHTML;
 
 var historyModule = require('./history');
 var writeHistory = historyModule.writeHistory;
@@ -757,7 +756,7 @@ function updateStatusBar() {
 }
 
 //ui vars
-var ui = {
+window.ui = {
     spinner: $(".ui-spinner"),
     content: $(".ui-content"),
     toolbar: {
diff --git a/public/js/pretty.js b/public/js/pretty.js
index 6b551c1..c521120 100644
--- a/public/js/pretty.js
+++ b/public/js/pretty.js
@@ -7,11 +7,11 @@ var renderTOC = extra.renderTOC;
 var generateToc = extra.generateToc;
 var smoothHashScroll = extra.smoothHashScroll;
 var postProcess = extra.postProcess;
-var lastchangeui = extra.lastchangeui;
 var updateLastChange = extra.updateLastChange;
+var parseMeta = extra.parseMeta;
 var preventXSS = require('./render').preventXSS;
 
-var markdown = $(".markdown-body");
+var markdown = $("#doc.markdown-body");
 var text = $('<textarea/>').html(markdown.html()).text();
 var lastMeta = md.meta;
 md.meta = {};
diff --git a/public/js/slide.js b/public/js/slide.js
index 257c60b..2536c60 100644
--- a/public/js/slide.js
+++ b/public/js/slide.js
@@ -14,7 +14,6 @@ var urlpath = commonModule.urlpath;
 var noteid = commonModule.noteid;
 var debug = commonModule.debug;
 var version = commonModule.version;
-var serverurl = commonModule.serverurl;
 var GOOGLE_API_KEY = commonModule.GOOGLE_API_KEY;
 var GOOGLE_CLIENT_ID = commonModule.GOOGLE_CLIENT_ID;
 var DROPBOX_APP_KEY = commonModule.DROPBOX_APP_KEY;
@@ -22,23 +21,10 @@ var noteurl = commonModule.noteurl;
 
 var extraModule = require('./extra');
 var md = extraModule.md;
-var createtime = extraModule.createtime;
 var updateLastChange = extraModule.updateLastChange;
-var postProcess = extraModule.postProcess;
 var finishView = extraModule.finishView;
-var lastchangetime = extraModule.lastchangetime;
-var lastchangeui = extraModule.lastchangeui;
-var autoLinkify = extraModule.autoLinkify;
-var generateToc = extraModule.generateToc;
-var smoothHashScroll = extraModule.smoothHashScroll;
-var lastchangeuser = extraModule.lastchangeuser;
-var deduplicatedHeaderId = extraModule.deduplicatedHeaderId;
-var renderTOC = extraModule.renderTOC;
-var renderTitle = extraModule.renderTitle;
-var renderFilename = extraModule.renderFilename;
-var scrollToHash = extraModule.scrollToHash;
 
-var render = require('./render');
+require('./render');
 
 var body = $(".slides").html();
 $(".slides").html(S(body).unescapeHTML().s);
diff --git a/public/js/syncscroll.js b/public/js/syncscroll.js
index 511c500..a9cfb5a 100644
--- a/public/js/syncscroll.js
+++ b/public/js/syncscroll.js
@@ -109,7 +109,8 @@ md.use(markdownitContainer, 'info', { render: renderContainer });
 md.use(markdownitContainer, 'warning', { render: renderContainer });
 md.use(markdownitContainer, 'danger', { render: renderContainer });
 
-var syncscroll = true;
+// FIXME: expose syncscroll to window
+window.syncscroll = true;
 
 var preventSyncScrollToEdit = false;
 var preventSyncScrollToView = false;
diff --git a/public/views/foot.ejs b/public/views/foot.ejs
index 696fe3b..4294070 100644
--- a/public/views/foot.ejs
+++ b/public/views/foot.ejs
@@ -18,9 +18,11 @@
 <script src="<%- url %>/vendor/mermaid/dist/mermaid.min.js" defer></script>
 <% } %>
 <script src="//ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
+<script src="//cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script>
 <script src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" defer></script>
 <script src="//cdnjs.cloudflare.com/ajax/libs/lodash.js/4.16.4/lodash.min.js" defer></script>
 <script src="//cdnjs.cloudflare.com/ajax/libs/socket.io/1.5.0/socket.io.min.js" defer></script>
+<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.0.5/handlebars.min.js" defer></script>
 <script src="//cdnjs.cloudflare.com/ajax/libs/viz.js/1.3.0/viz.js" defer></script>
 <%- include build/index-scripts %>
 <script src="<%- url %>/js/google-drive-upload.js" defer></script>
diff --git a/public/views/index.ejs b/public/views/index.ejs
index 0bc3dd5..4ab3cde 100644
--- a/public/views/index.ejs
+++ b/public/views/index.ejs
@@ -204,7 +204,10 @@
     <script src="<%- url %>/vendor/velocity/velocity.min.js" defer></script>
 	<script src="<%- url %>/vendor/moment/min/moment-with-locales.min.js" defer></script>
 	<% } %>
-	<script src="//ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js" defer></script>
+	<script src="//ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
+    <script src="//cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script>
+	<script src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" defer></script>
+    <script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.0.5/handlebars.min.js" defer></script>
     <script src="//cdnjs.cloudflare.com/ajax/libs/lodash.js/4.16.4/lodash.min.js" defer></script>
     <%- include build/cover-scripts %>
 </body>
diff --git a/public/views/pretty.ejs b/public/views/pretty.ejs
index 0cd9e01..8fecb04 100644
--- a/public/views/pretty.ejs
+++ b/public/views/pretty.ejs
@@ -99,8 +99,10 @@
 <script src="<%- url %>/vendor/moment/min/moment-with-locales.js" defer></script>
 <script src="<%- url %>/vendor/mermaid/dist/mermaid.min.js" defer></script>
 <% } %>
-<script src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" defer></script>
 <script src="//ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
+<script src="//cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script>
+<script src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" defer></script>
+<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.0.5/handlebars.min.js" defer></script>
 <script src="//cdnjs.cloudflare.com/ajax/libs/lodash.js/4.16.4/lodash.min.js" defer></script>
 <script src="//cdnjs.cloudflare.com/ajax/libs/viz.js/1.3.0/viz.js" defer></script>
 <%- include build/pretty-scripts %>
diff --git a/public/views/slide.ejs b/public/views/slide.ejs
index 79d7708..398f40b 100644
--- a/public/views/slide.ejs
+++ b/public/views/slide.ejs
@@ -102,6 +102,8 @@
         <script src="<%- url %>/vendor/mermaid/dist/mermaid.min.js" defer></script>
         <% } %>
         <script src="//ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
+        <script src="//cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script>
+        <script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.0.5/handlebars.min.js" defer></script>
         <script src="//cdnjs.cloudflare.com/ajax/libs/lodash.js/4.16.4/lodash.min.js" defer></script>
         <script src="//cdnjs.cloudflare.com/ajax/libs/viz.js/1.3.0/viz.js" defer></script>
         <%- include build/slide-scripts %>
diff --git a/webpackBaseConfig.js b/webpackBaseConfig.js
index e38213b..7648cd8 100644
--- a/webpackBaseConfig.js
+++ b/webpackBaseConfig.js
@@ -12,7 +12,9 @@ module.exports = {
             key: "keymaster",
             $: "jquery",
             jQuery: "jquery",
-            "window.jQuery": "jquery"
+            "window.jQuery": "jquery",
+            "moment": "moment",
+            "Handlebars": "handlebars"
         }),
         new ExtractTextPlugin("[name].css"),
         new webpack.optimize.CommonsChunkPlugin({
@@ -78,12 +80,7 @@ module.exports = {
         slide: path.join(__dirname, 'public/js/slide.js'),
         locale: path.join(__dirname, 'public/js/locale.js'),
         vendor: [
-            "jquery-mousewheel",
-            "jquery-scrollspy/jquery-scrollspy",
-            "jquery-ui/ui/widgets/resizable",
-            "jquery-ui/ui/widgets/tooltip",
-            "jquery-ui/ui/widgets/controlgroup",
-            "jquery-ui/ui/widgets/autocomplete",
+            "imports?$=jquery!jquery-mousewheel",
             "script!gist-embed",
             "expose?filterXSS!xss",
             "js-url",
@@ -130,7 +127,9 @@ module.exports = {
         "viz.js": "Viz",
         "socket.io-client": "io",
         "lodash": "_",
-        "jquery": "$"
+        "jquery": "$",
+        "moment": "moment",
+        "handlebars": "Handlebars"
     },
 
     module: {