diff --git a/package.json b/package.json index 2219058..cddc8d8 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,9 @@ "eslint-import-resolver-webpack": "^0.3.2", "eslint-plugin-babel": "^3.2.0", "eslint-plugin-import": "^1.7.0", + "exports-loader": "^0.6.3", "file-loader": "^0.9.0", + "imports-loader": "^0.6.5", "jsdoc": "^3.4.0", "ts-loader": "^0.8.2", "typedoc": "^0.4.4", @@ -63,7 +65,8 @@ "fullcalendar": "^2.9.0", "intl": "^1.2.4", "moment": "^2.14.1", - "validator": "^5.2.0" + "validator": "^5.2.0", + "whatwg-fetch": "^1.0.0" }, "nbbpm": { "compatibility": "^1.0.3" diff --git a/public/calendar.less b/public/calendar.less index 476c398..70a071b 100644 --- a/public/calendar.less +++ b/public/calendar.less @@ -1 +1,9 @@ @import (inline) '../node_modules/fullcalendar/dist/fullcalendar.css'; + +.fc-event { + cursor: pointer; +} + +#plugin-calendar-cal-event-display .plugin-calendar-event { + margin: -15px; +} diff --git a/public/languages/en_US/calendar.json b/public/languages/en_US/calendar.json index 337e835..d1f5490 100644 --- a/public/languages/en_US/calendar.json +++ b/public/languages/en_US/calendar.json @@ -6,6 +6,7 @@ "edit_event": "Edit Event", "event_starting": "Event starting %1: %2", "now": "now", + "go_to_post": "Go to post", "event_name": "Event name", "all_day": "All day", diff --git a/public/templates/calendar.tpl b/public/templates/calendar.tpl index 2be5d2e..076dc5c 100644 --- a/public/templates/calendar.tpl +++ b/public/templates/calendar.tpl @@ -1,6 +1,32 @@ -

[[calendar:calendar]]

-
- + + + diff --git a/src/calendar/index.js b/src/calendar/index.js index fde8603..114acb2 100644 --- a/src/calendar/index.js +++ b/src/calendar/index.js @@ -1,9 +1,7 @@ -/* global socket, $, config, app */ +/* global socket, $, config, app, RELATIVE_PATH, fetch */ import './vendor/fullcalendar'; -// TODO: centralized calendar interface - const convertToFC = event => { const ev = { id: event.pid, @@ -17,6 +15,14 @@ const convertToFC = event => { return ev; }; +const toggle = e => { + const panel = $(e.target).closest('.panel'); + const cont = panel.find('.panel-collapse'); + const height = cont.children()[0].scrollHeight; + cont.css('height', `${height}px`); + panel.toggleClass('closed'); +}; + const calendarOptions = { editable: false, header: { @@ -38,35 +44,74 @@ const calendarOptions = { }); }, eventClick: event => { - // TODO: show modal on event click + const pid = event.id; + fetch(`${RELATIVE_PATH}/api/post/${pid}`) + .then(response => response.json()) + .then(path => fetch(`${RELATIVE_PATH}/api${path}`)) + .then(response => response.json()) + .then(topic => topic.posts.find(post => parseInt(post.pid, 10) === pid).content) + .then(content => { + const $c = $(content); + let div = $c.filter('.plugin-calendar-event'); + if (!div.length) { + div = $c.find('.plugin-calendar-event'); + } + return div; + }) + .then(div => { + const modal = $('#plugin-calendar-cal-event-display'); + modal + .find('.modal-body') + .empty() + .append(div); + modal + .find('.modal-footer a.btn-primary') + .attr('href', `${RELATIVE_PATH}/post/${pid}`); + modal + .find('.modal-body .plugin-calendar-event-responses-lists .panel-heading a') + .on('click', toggle); + modal + .find('.plugin-calendar-event-responses-lists .panel-body') + .addClass('topic') + .find('ul') + .addClass('posts'); + $(window).trigger('action:calendar.event.display', { pid, modal }); + modal + .attr('data-pid', pid) + .modal('show'); + }); }, }; /* eslint-disable */ -function shadeColor2(color, percent) { - var f=parseInt(color.slice(1),16),t=percent<0?0:255,p=percent<0?percent*-1:percent,R=f>>16,G=f>>8&0x00FF,B=f&0x0000FF; - return "#"+(0x1000000+(Math.round((t-R)*p)+R)*0x10000+(Math.round((t-G)*p)+G)*0x100+(Math.round((t-B)*p)+B)).toString(16).slice(1); -} +// function shadeColor2(color, percent) { +// var f=parseInt(color.slice(1),16),t=percent<0?0:255,p=percent<0?percent*-1:percent,R=f>>16,G=f>>8&0x00FF,B=f&0x0000FF; +// return "#"+(0x1000000+(Math.round((t-R)*p)+R)*0x10000+(Math.round((t-G)*p)+G)*0x100+(Math.round((t-B)*p)+B)).toString(16).slice(1); +// } /* eslint-enable */ const init = () => { - socket.emit('plugins.calendar.getCategoryColors', (err, colors) => { - if (err) { - app.alertError(err); - return; - } - let style = ''; - $(document.head).append(style); - }); + // socket.emit('plugins.calendar.getCategoryColors', (err, colors) => { + // if (err) { + // app.alertError(err); + // return; + // } + // let style = ''; + // $(document.head).append(style); + // }); $('#calendar').fullCalendar(calendarOptions); + + $(window).on('action:ajaxify.end', () => { + // TODO: automatically go to and open event when pid is in hash of url + }); }; $(document).ready(init); diff --git a/src/client/responses.js b/src/client/responses.js index 1b9001a..911a1ea 100644 --- a/src/client/responses.js +++ b/src/client/responses.js @@ -77,7 +77,7 @@ const initResponses = () => { $(document.body).on('click', '.plugin-calendar-event-responses-user .btn', e => { const button = $(e.target); const value = button.data('value'); - const pid = button.closest('[component=post]').data('pid'); + const pid = button.closest('[data-pid]').data('pid'); socket.emit('plugins.calendar.submitResponse', { pid, value }, err => { if (err) { @@ -99,7 +99,46 @@ const initResponses = () => { } }; - $(window).on('action:posts.loaded action:ajaxify.end action:posts.edited', checkPosts); + $(window).on('action:calendar.event.display', (e, { pid, modal }) => { + const buttonCont = modal.find('.plugin-calendar-event-responses-user'); + socket.emit('plugins.calendar.getUserResponse', pid, (err, value) => { + const button = buttonCont.find(`[data-value=${value}]`); + + button.siblings().removeClass('active'); + button.addClass('active'); + }); + + const $responses = modal.find('.plugin-calendar-event-responses-lists'); + socket.emit('plugins.calendar.getResponses', pid, (err, responses) => { + if (err) { + app.alertError(err); + return; + } + if (!responses || + !responses.yes || + !responses.maybe || + !responses.no) { + return; + } + + const yess = $responses.find('.plugin-calendar-event-responses-list-yes'); + const maybes = $responses.find('.plugin-calendar-event-responses-list-maybe'); + const nos = $responses.find('.plugin-calendar-event-responses-list-no'); + + const yes = responses.yes.map(userTemplate); + const maybe = responses.maybe.map(userTemplate); + const no = responses.no.map(userTemplate); + + yess.empty().append(yes.length ? yes : noYesResponses); + maybes.empty().append(maybe.length ? maybe : noMaybeResponses); + nos.empty().append(no.length ? no : noNoResponses); + }); + }); + + $(window).on( + 'action:posts.loaded action:ajaxify.end action:posts.edited', + checkPosts + ); checkPosts(null, ajaxify.data); $(document.body).on( diff --git a/src/lib/controllers.js b/src/lib/controllers.js index b711547..2d00ef7 100644 --- a/src/lib/controllers.js +++ b/src/lib/controllers.js @@ -1,4 +1,19 @@ +const privileges = require.main.require('./src/privileges'); +const categories = require.main.require('./src/categories'); + // import { getEventsByDate, filterByPid, escapeEvent } from './event'; +import Promise from 'bluebird'; +const p = Promise.promisify; + +const getAllCategoryFields = p(categories.getAllCategoryFields); +const filterCids = p(privileges.categories.filterCids); + +/* eslint-disable */ +function shadeColor2(color, percent) { + var f=parseInt(color.slice(1),16),t=percent<0?0:255,p=percent<0?percent*-1:percent,R=f>>16,G=f>>8&0x00FF,B=f&0x0000FF; + return "#"+(0x1000000+(Math.round((t-R)*p)+R)*0x10000+(Math.round((t-G)*p)+G)*0x100+(Math.round((t-B)*p)+B)).toString(16).slice(1); +} +/* eslint-enable */ export default (router, middleware) => { const renderAdmin = (req, res) => { @@ -7,14 +22,14 @@ export default (router, middleware) => { router.get('/admin/plugins/calendar', middleware.admin.buildHeader, renderAdmin); router.get('/api/admin/plugins/calendar', renderAdmin); - const renderPage = (req, res) => { // next - // const cb = (err, data) => { - // if (err) { - // next(err); - // return; - // } - // res.render('calendar', data); - // }; + const renderPage = (req, res, next) => { + const cb = (err, data) => { + if (err) { + next(err); + return; + } + res.render('calendar', data); + }; // // const startDate = new Date(); // const endDate = new Date(); @@ -35,8 +50,26 @@ export default (router, middleware) => { // }; // })(req.uid).asCallback(cb); + (async () => { + const cats = await getAllCategoryFields(['cid', 'bgColor']); + const filtered = await filterCids('read', cats.map(c => c.cid), req.uid); + + const colors = cats.filter(c => filtered.includes(c.cid)); + + const style = colors.map(({ cid, bgColor }) => + `.plugin-calendar-cal-event-category-${cid} { + background-color: ${bgColor}; + border-color: ${shadeColor2(bgColor, -0.2)}; + }` + ); + + return { + calendarEventsStyle: style.join('\n'), + title: '[[calendar:calendar]]', + }; + })().asCallback(cb); - res.render('calendar', {}); + // res.render('calendar', {}); }; router.get('/calendar', middleware.buildHeader, renderPage); diff --git a/src/lib/sockets.js b/src/lib/sockets.js index 7ba575f..4294103 100644 --- a/src/lib/sockets.js +++ b/src/lib/sockets.js @@ -1,15 +1,8 @@ const privileges = require.main.require('./src/privileges'); const pluginSockets = require.main.require('./src/socket.io/plugins'); -const categories = require.main.require('./src/categories'); import { getAll as getAllResponses, submitResponse, getUserResponse } from './responses'; import { getEventsByDate, filterByPid, escapeEvent } from './event'; -import Promise from 'bluebird'; - -const p = Promise.promisify; - -const getAllCategoryFields = p(categories.getAllCategoryFields); -const filterCids = p(privileges.categories.filterCids); const perm = 'plugin-calendar:event:post'; @@ -46,12 +39,12 @@ pluginSockets.calendar.getUserResponse = ({ uid }, pid, cb) => { getUserResponse({ uid, pid }).asCallback(cb); }; -pluginSockets.calendar.getCategoryColors = ({ uid }, cb) => (async () => { - const cats = await getAllCategoryFields(['cid', 'bgColor']); - const filtered = await filterCids('read', cats.map(c => c.cid), uid); - - return cats.filter(c => filtered.includes(c.cid)); -})().asCallback(cb); +// pluginSockets.calendar.getCategoryColors = ({ uid }, cb) => (async () => { +// const cats = await getAllCategoryFields(['cid', 'bgColor']); +// const filtered = await filterCids('read', cats.map(c => c.cid), uid); +// +// return cats.filter(c => filtered.includes(c.cid)); +// })().asCallback(cb); pluginSockets.calendar.getEventsByDate = ({ uid }, { startDate, endDate }, cb) => (async () => { diff --git a/ts/index.js b/ts/index.js new file mode 100644 index 0000000..b770a7d --- /dev/null +++ b/ts/index.js @@ -0,0 +1 @@ +// TODO: convert to TypeScript? diff --git a/webpack.config.js b/webpack.config.js index dc5e3c2..78c8b11 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -1,4 +1,4 @@ -// const webpack = require('webpack'); +const webpack = require('webpack'); const path = require('path'); const nodeEnv = process.env.NODE_ENV || 'development'; @@ -54,26 +54,29 @@ module.exports = { path.resolve('./src/client/vendor'), ], }, - // plugins: [ - // // new webpack.optimize.CommonsChunkPlugin({ - // // name: 'vendor', - // // minChunks: Infinity, - // // filename: 'vendor.bundle.js', - // // }), - // new webpack.LoaderOptionsPlugin({ - // minimize: isProd, - // debug: !isProd, - // }), - // isProd && new webpack.optimize.UglifyJsPlugin({ - // compress: { - // warnings: false, - // }, - // output: { - // comments: false, - // }, - // sourceMap: false, - // }), - // ], + plugins: [ + // new webpack.optimize.CommonsChunkPlugin({ + // name: 'vendor', + // minChunks: Infinity, + // filename: 'vendor.bundle.js', + // }), + // new webpack.LoaderOptionsPlugin({ + // minimize: isProd, + // debug: !isProd, + // }), + // isProd && new webpack.optimize.UglifyJsPlugin({ + // compress: { + // warnings: false, + // }, + // output: { + // comments: false, + // }, + // sourceMap: false, + // }), + new webpack.ProvidePlugin({ + fetch: 'imports?this=>global!exports?global.fetch!whatwg-fetch', + }), + ], }; // console.log(module.exports);