Let Owners edit their Events in details-view
Resolved ticket #876997 (https://bugzilla.mozilla.org/show_bug.cgi?id=876997).
This commit is contained in:
Родитель
ce49addade
Коммит
41f0c248e8
|
@ -1,11 +1,11 @@
|
|||
@import "ui";
|
||||
|
||||
body#events .overlay-container {
|
||||
body#events.map .overlay-container {
|
||||
position: relative;
|
||||
margin: 0px auto;
|
||||
max-width: 980px;
|
||||
}
|
||||
body#events .map-overlay {
|
||||
body#events.map .map-overlay {
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
margin: 20px;
|
||||
|
@ -165,7 +165,6 @@ body#events .map-overlay {
|
|||
font-size: 13px;
|
||||
}
|
||||
.md-editor,input {
|
||||
background: white;
|
||||
outline: 0px;
|
||||
border: 0.5px solid #444;
|
||||
margin: 6px 4px;
|
||||
|
@ -182,48 +181,6 @@ body#events .map-overlay {
|
|||
margin-top: 8px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
.md-editor {
|
||||
background: #222;
|
||||
margin-left: -1px;
|
||||
padding: 4px;
|
||||
padding-top: 0px;
|
||||
clear: left;
|
||||
|
||||
textarea {
|
||||
height: 34ex;
|
||||
padding: 8px 10px;
|
||||
|
||||
font-family: inherit;
|
||||
font-size: 14px;
|
||||
}
|
||||
.md-preview {
|
||||
min-height: 5ex;
|
||||
padding: 8px 10px;
|
||||
|
||||
color: #333;
|
||||
}
|
||||
.md-header {
|
||||
background: #222;
|
||||
}
|
||||
.btn-group {
|
||||
display: inline-block;
|
||||
}
|
||||
button {
|
||||
.ui-btn;
|
||||
.clickable;
|
||||
background: inherit;
|
||||
border: 0px;
|
||||
border-radius: 1px;
|
||||
padding: 4px 8px;
|
||||
|
||||
color: #eee;
|
||||
&:hover {
|
||||
background: #fafafa;
|
||||
text-shadow: none;
|
||||
color: #222;
|
||||
}
|
||||
}
|
||||
}
|
||||
input[name="title"] {
|
||||
min-width: 21em;
|
||||
}
|
||||
|
@ -264,8 +221,3 @@ body#events .map-overlay {
|
|||
}
|
||||
}
|
||||
}
|
||||
.ui-datepicker {
|
||||
border: 1px solid #ddd;
|
||||
margin-left: -34.5px;
|
||||
margin-top: 11px;
|
||||
}
|
||||
|
|
|
@ -136,4 +136,20 @@ body#events.details {
|
|||
|
||||
border-radius: 0px 0px 4px 4px;
|
||||
}
|
||||
form {
|
||||
input {
|
||||
border: 1px solid #ccc;
|
||||
padding-left: 3px;
|
||||
padding-left: 4px;
|
||||
}
|
||||
input[name="title"] {
|
||||
padding-left: 10px;
|
||||
|
||||
font-weight: 300;
|
||||
font-size: 35px;
|
||||
}
|
||||
input[name="address"] {
|
||||
width: 21em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,6 @@ body#events {
|
|||
margin-top: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
.clear { clear: both; }
|
||||
.hidden { display: none; }
|
||||
.clickable {
|
||||
|
@ -33,3 +32,50 @@ body#events {
|
|||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
.ui-datepicker {
|
||||
border: 1px solid #ddd;
|
||||
margin-left: -34.5px;
|
||||
margin-top: 11px;
|
||||
}
|
||||
.md-editor {
|
||||
background: #222;
|
||||
margin-left: -1px;
|
||||
padding: 4px;
|
||||
padding-top: 0px;
|
||||
clear: left;
|
||||
|
||||
textarea {
|
||||
height: 34ex;
|
||||
padding: 8px 10px;
|
||||
|
||||
font-family: inherit;
|
||||
font-size: 14px;
|
||||
}
|
||||
.md-preview {
|
||||
min-height: 5ex;
|
||||
padding: 8px 10px;
|
||||
|
||||
color: #333;
|
||||
}
|
||||
.md-header {
|
||||
background: #222;
|
||||
}
|
||||
.btn-group {
|
||||
display: inline-block;
|
||||
}
|
||||
button {
|
||||
.ui-btn;
|
||||
.clickable;
|
||||
background: inherit;
|
||||
border: 0px;
|
||||
border-radius: 1px;
|
||||
padding: 4px 8px;
|
||||
|
||||
color: #eee;
|
||||
&:hover {
|
||||
background: #fafafa;
|
||||
text-shadow: none;
|
||||
color: #222;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,14 @@ module.exports = function () {
|
|||
+ '/' + filename
|
||||
: this.client.url(filename)
|
||||
},
|
||||
delete: function (url) {
|
||||
var match = url.match(/\/([^\/]+)\/?$/);
|
||||
if (match) {
|
||||
this.client.del(match[1]);
|
||||
return true;
|
||||
} else console.error("Error: S3 url ("+url+") seems to be invalid.");
|
||||
return false;
|
||||
},
|
||||
client: noxmox[s3_mode].createClient(s3_conf)
|
||||
};
|
||||
};
|
||||
|
|
|
@ -28,64 +28,21 @@ module.exports = function (init) {
|
|||
},
|
||||
create: function(req, res)
|
||||
{
|
||||
var event = req.body.event || req.body;
|
||||
console.log(event);
|
||||
|
||||
var fields = { // undefined fields are required
|
||||
title: undefined,
|
||||
description: undefined,
|
||||
address: undefined,
|
||||
organizer: undefined,
|
||||
organizerId: undefined,
|
||||
latitude: null,
|
||||
longitude: null,
|
||||
attendees: 3,
|
||||
beginDate: null,
|
||||
endDate: null,
|
||||
beginTime: null,
|
||||
endTime: null,
|
||||
registerLink: null
|
||||
};
|
||||
var required = [ 'title', 'description', 'latitude', 'longitude', 'address' ];
|
||||
var event = event_input_filter(req.body.event || req.body);
|
||||
if (!event)
|
||||
return res.reply(400, 'Invalid Event provided');
|
||||
|
||||
if (!(event.organizer = req.session.email))
|
||||
return res.reply(401, 'Log in to create Events');
|
||||
event.organizerId = req.session.username;
|
||||
if (event.picture) {
|
||||
var match = event.picture.match(/^data:(image\/[\w+-]+);.*?base64,(.*)/);
|
||||
event.picture = match ? {
|
||||
type: match[1],
|
||||
data: new Buffer(match[2], 'base64')
|
||||
} : null
|
||||
}
|
||||
// pre-process the Date/Time fields
|
||||
['begin', 'end'].forEach(function (pfx) {
|
||||
datetime_transform('Date', function (val) {
|
||||
return new Date(val.split('-'));
|
||||
});
|
||||
datetime_transform('Time', function (val) {
|
||||
var ts = val.split(':');
|
||||
return new Date(0, 0, 0, ts[0], ts[1]);
|
||||
});
|
||||
function datetime_transform(f, transform) {
|
||||
var dtf = pfx + f;
|
||||
event[dtf] = (function(event) {
|
||||
if (!event[dtf]) return null;
|
||||
var new_time = transform(event[dtf]);
|
||||
return new_time != "Invalid Date" ? new_time : null;
|
||||
})(event)
|
||||
}
|
||||
});
|
||||
function empty(x) { return x === '' || x === undefined }
|
||||
var trns_event = {};
|
||||
Object.keys(fields).forEach(function (f) {
|
||||
trns_event[f] = empty(event[f]) ? fields[f] : event[f];
|
||||
});
|
||||
if (!required.every(function (f) { return !empty(trns_event[f]) }))
|
||||
return res.reply(400, 'Invalid Event provided');
|
||||
|
||||
var picture = event.picture;
|
||||
Event.create(trns_event, Object.keys(fields)).success(function (event) {
|
||||
delete event.picture;
|
||||
|
||||
var fields = ['title', 'description', 'address', 'latitude',
|
||||
'longitude', 'attendees', 'beginDate', 'endDate', 'beginTime',
|
||||
'endTime', 'registerLink', 'organizer', 'organizerId'];
|
||||
Event.create(trns_event, fields).success(function (event) {
|
||||
if (picture) {
|
||||
var filename = uuid.v4();
|
||||
var s3_req = s3.client.put(filename, {
|
||||
|
@ -108,48 +65,60 @@ module.exports = function (init) {
|
|||
},
|
||||
details: function(req, res)
|
||||
{
|
||||
Event.find(req.params.id).success(function (event) {
|
||||
if (!event) return res.reply(404, 'Event not found');
|
||||
fetch_event(req, function (event) {
|
||||
res.format({
|
||||
json: function () { res.reply(200, { event: event }) },
|
||||
html: function () {
|
||||
function fmtDate(x) { return new Date(x).toDateString() }
|
||||
function fmtTime(x) { return new Date(x).toTimeString().split(' ')[0] }
|
||||
|
||||
var evt = {};
|
||||
for (var p in event) switch(p) {
|
||||
case 'beginDate':
|
||||
case 'endDate':
|
||||
evt[p] = fmtDate(event[p]);
|
||||
break;
|
||||
case 'beginTime':
|
||||
case 'endTime':
|
||||
evt[p] = fmtTime(event[p]);
|
||||
break;
|
||||
case 'description':
|
||||
evt[p] = markdown.toHTML(event[p]);
|
||||
default:
|
||||
evt[p] = event[p];
|
||||
}
|
||||
res.reply('details', { event: evt });
|
||||
res.reply('details', { event: event_output_filter(event) });
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
update: function(req, res)
|
||||
change: function(req, res)
|
||||
{
|
||||
Event.find(req.params.id).success(function (event) {
|
||||
event.updateAttributes(req.params.id).success(function () {
|
||||
res.reply(200, 'Event updated', { event: event });
|
||||
console.log(req.body.event || req.body);
|
||||
var changes = event_input_filter(req.body.event || req.body, false, true, false);
|
||||
console.log(changes);
|
||||
if (!changes)
|
||||
return res.reply(400, 'Invalid Event changes requested');
|
||||
var allowed = [ 'title', 'description', 'address', 'latitude',
|
||||
'longitude', 'attendees', 'beginDate', 'endDate',
|
||||
'beginTime', 'endTime', 'registerLink' ];
|
||||
|
||||
Object.keys(changes).forEach(function (k) {
|
||||
if (empty(changes[k]))
|
||||
delete changes[k];
|
||||
});
|
||||
var picture = changes.picture;
|
||||
fetch_event(req, function (event) {
|
||||
event.updateAttributes(changes, allowed).success(function () {
|
||||
if (picture) {
|
||||
var filename = uuid.v4();
|
||||
var s3_req = s3.client.put(filename, {
|
||||
'Content-Length': picture.data.length,
|
||||
'Content-Type': picture.type,
|
||||
'x-amz-acl': 'public-read'
|
||||
});
|
||||
s3_req.on('response', function(s3_res) {
|
||||
if (s3_res.statusCode === 200)
|
||||
s3.delete(event.picture);
|
||||
event.updateAttributes({
|
||||
picture: s3.url(filename)
|
||||
});
|
||||
});
|
||||
s3_req.end(picture.data);
|
||||
}
|
||||
res.reply(200, 'Event modified', { event: event });
|
||||
});
|
||||
}).error(function (err) {
|
||||
res.reply(404, 'Event not found');
|
||||
});
|
||||
},
|
||||
destroy: function(req, res)
|
||||
{
|
||||
Event.find(req.params.id).success(function (event) {
|
||||
var picture = event.picture;
|
||||
event.destroy().success(function () {
|
||||
if (picture)
|
||||
s3.delete(picture);
|
||||
res.reply(200, 'Event deleted');
|
||||
});
|
||||
}).error(function (err) {
|
||||
|
@ -157,4 +126,103 @@ module.exports = function (init) {
|
|||
});
|
||||
}
|
||||
};
|
||||
|
||||
function empty(x) { return x === '' || x === undefined }
|
||||
function event_input_filter(event, set_defaults, do_transforms, check_required) {
|
||||
if (!event) return null;
|
||||
|
||||
set_defaults = set_defaults === undefined ? true : set_defaults;
|
||||
do_transforms = do_transforms === undefined ? true : do_transforms;
|
||||
check_required = check_required === undefined ? true : check_required;
|
||||
|
||||
var fields = { // undefined fields are required
|
||||
title: undefined,
|
||||
description: undefined,
|
||||
address: undefined,
|
||||
latitude: null,
|
||||
longitude: null,
|
||||
attendees: 3,
|
||||
beginDate: null,
|
||||
endDate: null,
|
||||
beginTime: null,
|
||||
endTime: null,
|
||||
registerLink: null
|
||||
};
|
||||
var required = [ 'title', 'description', 'latitude', 'longitude', 'address' ];
|
||||
|
||||
var transforms = {
|
||||
picture: function (event) {
|
||||
if (!event.picture) return;
|
||||
var match = event.picture.match(/^data:(image\/[\w+-]+);.*?base64,(.*)/);
|
||||
return match ? {
|
||||
type: match[1],
|
||||
data: new Buffer(match[2], 'base64')
|
||||
} : undefined
|
||||
}
|
||||
};
|
||||
// pre-process the Date/Time fields
|
||||
['begin', 'end'].forEach(function (pfx) {
|
||||
datetime_transform('Date', function (val) {
|
||||
return new Date(val.split('-'));
|
||||
});
|
||||
datetime_transform('Time', function (val) {
|
||||
var ts = val.split(':');
|
||||
return new Date(0, 0, 0, ts[0], ts[1]);
|
||||
});
|
||||
function datetime_transform(f, transform) {
|
||||
var dtf = pfx + f;
|
||||
transforms[dtf] = function(event) {
|
||||
if (!event[dtf]) return;
|
||||
var new_time = transform(event[dtf]);
|
||||
if (new_time != "Invalid Date")
|
||||
return new_time;
|
||||
};
|
||||
}
|
||||
});
|
||||
var evt = {};
|
||||
if (do_transforms)
|
||||
Object.keys(transforms).forEach(function (f) {
|
||||
evt[f] = transforms[f](event);
|
||||
});
|
||||
console.log(evt);
|
||||
Object.keys(fields).forEach(function (f) {
|
||||
if (!(f in evt)) evt[f] = event[f];
|
||||
});
|
||||
if (set_defaults)
|
||||
Object.keys(fields).forEach(function (f) {
|
||||
evt[f] = empty(event[f]) ? fields[f] : event[f];
|
||||
});
|
||||
if (check_required)
|
||||
evt = required.every(function (f) { return !empty(evt[f]) }) ? evt : null;
|
||||
return evt
|
||||
}
|
||||
function event_output_filter(event) {
|
||||
function fmtDate(x) { return new Date(x).toDateString() }
|
||||
function fmtTime(x) { return new Date(x).toTimeString().split(' ')[0] }
|
||||
|
||||
var evt = {};
|
||||
for (var p in event) switch(p) {
|
||||
case 'beginDate':
|
||||
case 'endDate':
|
||||
evt[p] = fmtDate(event[p]);
|
||||
break;
|
||||
case 'beginTime':
|
||||
case 'endTime':
|
||||
evt[p] = fmtTime(event[p]);
|
||||
break;
|
||||
case 'description':
|
||||
evt[p] = markdown.toHTML(event[p]);
|
||||
default:
|
||||
evt[p] = event[p];
|
||||
}
|
||||
return evt;
|
||||
}
|
||||
function fetch_event(req, success) {
|
||||
return Event.find(req.params.id).success(function (event) {
|
||||
if (!event) return req.res.reply(404, 'Event not found');
|
||||
success(event);
|
||||
}).error(function (err) {
|
||||
req.res.reply(404, 'Event not found');
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
1
index.js
1
index.js
|
@ -38,6 +38,7 @@ exports.init = function (app, nunjucksEnv, lessMiddleware, app_root) {
|
|||
|
||||
// Controllers
|
||||
ctx.controllers = require('./controllers').call(ctx, app);
|
||||
app.use(express.methodOverride());
|
||||
process.nextTick(require('./routes').bind(ctx, ctx.controllers, app));
|
||||
|
||||
// Handy shortcuts
|
||||
|
|
11
routes.js
11
routes.js
|
@ -3,9 +3,10 @@ module.exports = function (C, app) {
|
|||
app[method.toLowerCase()](path + '.:format?', action);
|
||||
}
|
||||
|
||||
route( 'GET', '/events', C.Events.index );
|
||||
route( 'GET', '/events/:id', C.Events.details );
|
||||
route( 'POST', '/events', C.Events.create );
|
||||
route( 'PUT', '/events/:id', C.Events.update );
|
||||
route( 'DELETE', '/events/:id', C.Events.destroy );
|
||||
route( 'GET', '/events', C.Events.index );
|
||||
route( 'GET', '/events/:id', C.Events.details );
|
||||
route( 'POST', '/events', C.Events.create );
|
||||
route( 'PATCH', '/events/:id', C.Events.change );
|
||||
//route( 'PUT', '/events/:id', C.Events.update );
|
||||
route( 'DELETE', '/events/:id', C.Events.destroy );
|
||||
};
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
define(['jquery', 'forms', 'domReady!'],
|
||||
function ($, forms) {
|
||||
var $editForm = $('form#edit-event');
|
||||
function toggleEditMode() {
|
||||
$('.show').toggleClass('hidden');
|
||||
$('.edit').toggleClass('hidden');
|
||||
}
|
||||
$editForm.find('button#edit-mode').click(function(ev) {
|
||||
toggleEditMode();
|
||||
});
|
||||
$editForm.find('button#cancel-edit').click(function(ev) {
|
||||
toggleEditMode();
|
||||
});
|
||||
$editForm.find('button#delete-event').click(function(ev) {
|
||||
// TODO: show modal with confirmation
|
||||
});
|
||||
});
|
|
@ -1,146 +1,10 @@
|
|||
define(['jquery', 'model', '../base/ui', 'bootstrap-markdown', 'jquery.timepicker', 'jquery-ui.datepicker', 'domReady!'],
|
||||
function ($, EventModel, UI) { return function (mapMaker) {
|
||||
$.event.props.push('dataTransfer');
|
||||
var $createForm = $('form#create-event');
|
||||
var $findForm = $('form#find-event');
|
||||
define(['jquery', '../base/ui', 'bootstrap-markdown', 'jquery.timepicker', 'jquery-ui.datepicker', 'domReady!'],
|
||||
function ($, UI) {
|
||||
|
||||
var $fileInput = $createForm.find('input[type="file"]');
|
||||
var $uploadDiv = $createForm.find('#image-upload');
|
||||
$uploadDiv.on("click", function(ev) {
|
||||
ev.preventDefault();
|
||||
$fileInput.click();
|
||||
}).on("dragenter dragover drop", function(ev) {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
}).on("drop", function(ev) {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
handleImg(ev.dataTransfer.files[0]);
|
||||
});
|
||||
$fileInput.on("change", function (ev) {
|
||||
handleImg(this.files[0]);
|
||||
});
|
||||
// based on MDN example
|
||||
function handleImg(file) {
|
||||
if (file.type.match(/^image\//)) {
|
||||
if (!$uploadDiv._prev_text)
|
||||
$uploadDiv._prev_text = $uploadDiv.text();
|
||||
$uploadDiv.html("<img />");
|
||||
var img = $uploadDiv.find("img")[0];
|
||||
img.file = file;
|
||||
|
||||
var reader = new FileReader();
|
||||
reader.onload = (function(img) {
|
||||
return function(ev) {
|
||||
img.src = ev.target.result;
|
||||
$createForm.find('input[name="picture"]').prop('value', img.src);
|
||||
};
|
||||
})(img);
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
}
|
||||
|
||||
$createForm.find('button[type="submit"]').click(function (ev) {
|
||||
ev.preventDefault();
|
||||
$createForm.submit();
|
||||
});
|
||||
$createForm.on("submit", function(ev) {
|
||||
ev.preventDefault();
|
||||
var form_fields = $createForm.serializeArray();
|
||||
var data = { event: {} };
|
||||
form_fields.forEach(function (f) {
|
||||
if (f.name) switch (f.name) {
|
||||
case '_csrf':
|
||||
data[f.name] = f.value;
|
||||
break;
|
||||
case 'address': // TODO: split address into two lines
|
||||
default:
|
||||
data.event[f.name] = f.value;
|
||||
}
|
||||
});
|
||||
$.post($createForm.attr('action'), data, function (data) {
|
||||
console.log(data.event);
|
||||
if (data.event) {
|
||||
toggleCreateForm();
|
||||
mapMaker.addMarker(new EventModel(data.event));
|
||||
}
|
||||
}, 'json');
|
||||
return false;
|
||||
});
|
||||
|
||||
$findForm.find('button[type="submit"]').click(function (ev) {
|
||||
ev.preventDefault();
|
||||
$findForm.submit();
|
||||
});
|
||||
var $when = $findForm.find('input[name="find-when"]');
|
||||
$when.blur(function(ev) { $findForm.submit() });
|
||||
mmm = mapMaker;
|
||||
$findForm.on("submit", function(ev) {
|
||||
ev.preventDefault();
|
||||
EventModel.all(function (models) {
|
||||
mapMaker.clearMarkers();
|
||||
var targetDateStr = $when[0].value;
|
||||
if (!targetDateStr)
|
||||
mapMaker.dropPins(models, false);
|
||||
else {
|
||||
var targetDate = new Date(targetDateStr.split('-'));
|
||||
mapMaker.dropPins(models, false, function (model) {
|
||||
var beginDate = new Date(model.beginDate),
|
||||
endDate = new Date(model.endDate);
|
||||
if (model.beginDate && model.endDate)
|
||||
return beginDate <= targetDate
|
||||
&& endDate >= targetDate
|
||||
else if (beginDate)
|
||||
return beginDate <= targetDate
|
||||
else if (endDate)
|
||||
return endDate >= targetDate
|
||||
else return true
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
// setup form toggle button
|
||||
function toggleCreateForm() {
|
||||
var $select = $('select[name="attendees"]');
|
||||
$createForm[0].reset();
|
||||
$select.next('.ui-select').remove();
|
||||
UI.select($select);
|
||||
$createForm.find('input[name="picture"]').prop('value', '');
|
||||
$uploadDiv.find('> img').prop('src', '');
|
||||
$uploadDiv.text($uploadDiv._prev_text);
|
||||
|
||||
$createForm.toggleClass('hidden');
|
||||
$("#add-event-button").toggleClass('hidden');
|
||||
}
|
||||
$(".formExpandButton").click(function(ev) {
|
||||
ev.preventDefault();
|
||||
toggleCreateForm();
|
||||
});
|
||||
|
||||
mapMaker.setupAutocomplete($('input[name="address"]')[0], false, function (place) {
|
||||
var loc = { latitude: place.geometry.location.lat(),
|
||||
longitude: place.geometry.location.lng() };
|
||||
for (var k in loc)
|
||||
$('form#create-event').find('input[name="'+k+'"]').val(loc[k]);
|
||||
});
|
||||
mapMaker.setupAutocomplete($('input[name="find-where"]')[0], true, function (place) {
|
||||
if (place.geometry) {
|
||||
// If the place has a geometry, then present it on a map.
|
||||
if (place.geometry.viewport) {
|
||||
this.google_map.fitBounds(place.geometry.viewport);
|
||||
} else {
|
||||
this.google_map.setCenter(place.geometry.location);
|
||||
this.google_map.setZoom(14);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$createForm.find('.datepicker').datepicker().each(function(i, elem) {
|
||||
$('.datepicker').datepicker().each(function(i, elem) {
|
||||
$(elem).next('.icon').click(function () { $(elem).focus() });
|
||||
});
|
||||
$createForm.find('.timepicker').timepicker().each(function(i, elem) {
|
||||
$('.timepicker').timepicker().each(function(i, elem) {
|
||||
$(elem).next('.icon').click(function () { $(elem).focus() });
|
||||
}).on('showTimepicker', function () {
|
||||
var $parent = $(this).parent();
|
||||
|
@ -150,11 +14,7 @@ function ($, EventModel, UI) { return function (mapMaker) {
|
|||
});
|
||||
var $beginTime = $('[name="beginTime"]'),
|
||||
$endTime = $('[name="endTime"]');
|
||||
$beginTime.timepicker({
|
||||
appendTo: function (elem) { return $(elem).parent() }
|
||||
});
|
||||
$endTime.timepicker({
|
||||
appendTo: function (elem) { return $(elem).parent() },
|
||||
durationTime: function () {
|
||||
var beginTime = $beginTime.timepicker('getTime');
|
||||
return beginTime ? beginTime : '0:00am';
|
||||
|
@ -162,7 +22,58 @@ function ($, EventModel, UI) { return function (mapMaker) {
|
|||
showDuration: true
|
||||
});
|
||||
|
||||
EventModel.all(function (models) { mapMaker.dropPins(models) });
|
||||
|
||||
window.scroll(0,0);
|
||||
}})
|
||||
|
||||
return {
|
||||
setupImageUpload: function ($form) {
|
||||
var $fileInput = $form.find('input[type="file"]');
|
||||
var $uploadDiv = $form.find('#image-upload');
|
||||
$.event.props.push('dataTransfer');
|
||||
$uploadDiv.on("click", function(ev) {
|
||||
ev.preventDefault();
|
||||
$fileInput.click();
|
||||
}).on("dragenter dragover drop", function(ev) {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
}).on("drop", function(ev) {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
handleImg(ev.dataTransfer.files[0]);
|
||||
});
|
||||
$fileInput.on("change", function (ev) {
|
||||
handleImg(this.files[0]);
|
||||
});
|
||||
// based on MDN example
|
||||
function handleImg(file) {
|
||||
if (file.type.match(/^image\//)) {
|
||||
if (!$uploadDiv._prev_text)
|
||||
$uploadDiv._prev_text = $uploadDiv.text();
|
||||
$uploadDiv.html("<img />");
|
||||
var img = $uploadDiv.find("img")[0];
|
||||
img.file = file;
|
||||
|
||||
var reader = new FileReader();
|
||||
reader.onload = (function(img) {
|
||||
return function(ev) {
|
||||
img.src = ev.target.result;
|
||||
$form.find('input[name="picture"]').prop('value', img.src);
|
||||
};
|
||||
})(img);
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
}
|
||||
$form.on('reset', function () {
|
||||
$uploadDiv.find('> img').prop('src', '');
|
||||
$uploadDiv.text($uploadDiv._prev_text);
|
||||
$form.find('input[name="picture"]').prop('value', '');
|
||||
});
|
||||
},
|
||||
setupSelectUI: function ($form) {
|
||||
$form.on('reset', function () {
|
||||
var $select = $('select[name="attendees"]');
|
||||
$select.next('.ui-select').remove();
|
||||
UI.select($select);
|
||||
});
|
||||
},
|
||||
};
|
||||
})
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
require.config({
|
||||
deps: ['main', 'jquery.css3finalize', 'jquery-ui.datepicker', 'bootstrap-markdown'],
|
||||
deps: ['main', 'jquery.css3finalize', 'bootstrap-markdown', 'jquery.timepicker', 'jquery-ui.datepicker'],
|
||||
paths: {
|
||||
'html': '/js/html',
|
||||
'base': '/js/base',
|
||||
|
|
|
@ -0,0 +1,97 @@
|
|||
define(['jquery', 'model', 'forms', 'bootstrap-markdown', 'domReady!'],
|
||||
function ($, EventModel, forms) { return function (mapMaker) {
|
||||
var $findForm = $('form#find-event');
|
||||
$findForm.find('button[type="submit"]').click(function (ev) {
|
||||
ev.preventDefault();
|
||||
$findForm.submit();
|
||||
});
|
||||
var $when = $findForm.find('input[name="find-when"]');
|
||||
$when.blur(function(ev) { $findForm.submit() });
|
||||
$findForm.on("submit", function(ev) {
|
||||
ev.preventDefault();
|
||||
EventModel.all(function (models) {
|
||||
mapMaker.clearMarkers();
|
||||
var targetDateStr = $when[0].value;
|
||||
if (!targetDateStr)
|
||||
mapMaker.dropPins(models, false);
|
||||
else {
|
||||
var targetDate = new Date(targetDateStr.split('-'));
|
||||
mapMaker.dropPins(models, false, function (model) {
|
||||
var beginDate = new Date(model.beginDate),
|
||||
endDate = new Date(model.endDate);
|
||||
if (model.beginDate && model.endDate)
|
||||
return beginDate <= targetDate
|
||||
&& endDate >= targetDate
|
||||
else if (beginDate)
|
||||
return beginDate <= targetDate
|
||||
else if (endDate)
|
||||
return endDate >= targetDate
|
||||
else return true
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
var $createForm = $('form#create-event');
|
||||
$createForm.on('submit', function(ev) {
|
||||
ev.preventDefault();
|
||||
var form_fields = $createForm.serializeArray();
|
||||
var data = { event: {} };
|
||||
form_fields.forEach(function (f) {
|
||||
if (f.name) switch (f.name) {
|
||||
case '_csrf':
|
||||
data[f.name] = f.value;
|
||||
break;
|
||||
case 'address': // TODO: split address into two lines
|
||||
default:
|
||||
data.event[f.name] = f.value;
|
||||
}
|
||||
});
|
||||
$.post($createForm.attr('action'), data, function (data) {
|
||||
console.log(data.event);
|
||||
if (data.event) {
|
||||
toggleCreateForm();
|
||||
mapMaker.addMarker(new EventModel(data.event));
|
||||
}
|
||||
}, 'json');
|
||||
return false;
|
||||
});
|
||||
var $beginTime = $createForm.find('[name="beginTime"]'),
|
||||
$endTime = $createForm.find('[name="endTime"]');
|
||||
$beginTime.timepicker({ appendTo: function (elem) { return $(elem).parent() } });
|
||||
$endTime.timepicker({ appendTo: function (elem) { return $(elem).parent() } });
|
||||
|
||||
forms.setupImageUpload($createForm);
|
||||
forms.setupSelectUI($createForm);
|
||||
|
||||
// setup form toggle button
|
||||
function toggleCreateForm() {
|
||||
$createForm[0].reset();
|
||||
$createForm.toggleClass('hidden');
|
||||
$("#add-event-button").toggleClass('hidden');
|
||||
}
|
||||
$(".formExpandButton").click(function(ev) {
|
||||
ev.preventDefault();
|
||||
toggleCreateForm();
|
||||
});
|
||||
|
||||
mapMaker.setupAutocomplete($('input[name="address"]')[0], false, function (place) {
|
||||
var loc = { latitude: place.geometry.location.lat(),
|
||||
longitude: place.geometry.location.lng() };
|
||||
for (var k in loc)
|
||||
$createForm.find('input[name="'+k+'"]').val(loc[k]);
|
||||
});
|
||||
mapMaker.setupAutocomplete($('input[name="find-where"]')[0], true, function (place) {
|
||||
if (place.geometry) {
|
||||
// If the place has a geometry, then present it on a map.
|
||||
if (place.geometry.viewport) {
|
||||
this.google_map.fitBounds(place.geometry.viewport);
|
||||
} else {
|
||||
this.google_map.setCenter(place.geometry.location);
|
||||
this.google_map.setZoom(14);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
EventModel.all(function (models) { mapMaker.dropPins(models) });
|
||||
}});
|
|
@ -6,7 +6,7 @@ require.config({
|
|||
'markerclusterer': 'map/markerclusterer',
|
||||
}
|
||||
});
|
||||
define(['jquery', 'google', 'map/map_maker', 'forms'],
|
||||
define(['jquery', 'google', 'map/map_maker', 'map/forms'],
|
||||
function ($, google, MapMaker, EventForms) {
|
||||
|
||||
var defaultZoom = 13;
|
||||
|
|
|
@ -3,62 +3,93 @@
|
|||
{% block title %}{{ event.title }}{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<div class="ui-wrapper ui-body">
|
||||
{{ super() }}
|
||||
<form id="edit-event" action="/events/{{ event.id }}" method="POST">
|
||||
<input name="_csrf" value="{{ csrf }}" type="hidden">
|
||||
|
||||
<div class="ui-wrapper ui-body">
|
||||
<div id="details-pane" class="ui-section">
|
||||
<div class="gallery-sidebar">
|
||||
{% if event.picture %}
|
||||
<img id="event-picture" src="{{ event.picture }}" onerror="this.style.display='none'" />
|
||||
{% endif %}
|
||||
{% if event.registerLink %}
|
||||
<div id="event-registration">
|
||||
<a id="registerLink" href="{{ event.registerLink }}">Sign up for the event <span class="icon-chevron-right"></span></a>
|
||||
<p>This link will take you to an external event registration service, such as Eventbrite.</p>
|
||||
<div class="gallery-sidebar">
|
||||
{% if event.picture %}
|
||||
<img id="event-picture" src="{{ event.picture }}" onerror="this.style.display='none'" />
|
||||
{% endif %}
|
||||
{% if event.registerLink %}
|
||||
<div id="event-registration">
|
||||
<a id="registerLink" href="{{ event.registerLink }}">Sign up for the event <span class="icon-chevron-right"></span></a>
|
||||
<p>This link will take you to an external event registration service, such as Eventbrite.</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<span><div id="event-description">
|
||||
<h1 class="show">{{ event.title }}</h1>
|
||||
<span class="edit hidden">
|
||||
<input type="text" name="title" placeholder="{{ event.title }}" />
|
||||
</span>
|
||||
<div id="event-whenwhere">
|
||||
<div class="temporal-local">
|
||||
<img class="calendar-icon" src="/img/map/calendar.png" />
|
||||
<div class="datetime-range">
|
||||
<span class="show">
|
||||
<div>
|
||||
{{ event.beginDate }}
|
||||
{% if event.beginDate and event.endDate %} - {% endif %}
|
||||
{{ event.endDate }}
|
||||
</div>
|
||||
<div>
|
||||
{{ event.beginTime }}
|
||||
{% if event.beginTime and event.endTime %} - {% endif %}
|
||||
{{ event.endTime }}
|
||||
</div>
|
||||
</span>
|
||||
<span class="edit hidden">
|
||||
<div> <input type="text" name="beginDate" class="datepicker" placeholder="{{ event.beginDate }}" />
|
||||
- <input type="text" name="endDate" class="datepicker" placeholder="{{ event.endDate }}" />
|
||||
</div>
|
||||
<div> <input type="text" name="beginTime" class="timepicker" placeholder="{{ event.beginTime }}" />
|
||||
- <input type="text" name="endTime" class="timepicker" placeholder="{{ event.endTime }}" />
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="temporal-local">
|
||||
<img class="pin-icon" src="/img/map/pin-event-red.png" />
|
||||
{% if event.address %}
|
||||
<span class="show">
|
||||
<div class="address">{{ event.address }}</div>
|
||||
</span>
|
||||
{% endif %}
|
||||
<span class="edit hidden">
|
||||
<input type="text" name="address" class="edit address" placeholder="{{ event.address }}" />
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<span><div id="event-description">
|
||||
<h1>{{ event.title }}</h1>
|
||||
<div id="event-whenwhere">
|
||||
<div class="temporal-local">
|
||||
{% if event.beginDate or event.endDate %}
|
||||
<img class="calendar-icon" src="/img/map/calendar.png" />
|
||||
{% endif %}
|
||||
<div class="datetime-range">
|
||||
<div> {{ event.beginDate }}
|
||||
{% if event.beginDate and event.endDate %} - {% endif %}
|
||||
{{ event.endDate }} </div>
|
||||
<div> {{ event.beginTime }}
|
||||
{% if event.beginTime and event.endTime %} - {% endif %}
|
||||
{{ event.endTime }} </div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="temporal-local">
|
||||
{% if event.address %}
|
||||
<img class="pin-icon" src="/img/map/pin-event-red.png" />
|
||||
<div class="address">
|
||||
<div> {{ event.address }} </div>
|
||||
<div> {{ event.city }} </div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% if event.organizer %}
|
||||
<div id="event-organizer">
|
||||
<img src="http://lorempixel.com/25/25/people/" />
|
||||
<span>Organized by <strong class="organizer">{{ event.organizerId }}</strong></span>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if email and event.organizer == email %}
|
||||
<button id="edit-event" class="owner-btn ui-btn">Edit</button>
|
||||
<button id="delete-event" class="owner-btn ui-btn">Delete</button>
|
||||
{% endif %}
|
||||
<p>{{ event.description }}</p>
|
||||
<div id="event-organizer">
|
||||
<img src="{{ event.organizerImg }}" />
|
||||
<span>Organized by <strong class="organizer">{{ event.organizerId }}</strong></span>
|
||||
</div>
|
||||
{% if email and event.organizer == email %}
|
||||
<div>
|
||||
<span class="show">
|
||||
<button id="edit-mode" type="button" class="owner-btn ui-btn">Edit</button>
|
||||
</span>
|
||||
<span class="edit hidden">
|
||||
<button id="save-edit" class="owner-btn ui-btn"
|
||||
type="submit" name="_method" value="PATCH">Save</button>
|
||||
<button id="cancel-edit" type="button" class="owner-btn ui-btn">Cancel</button>
|
||||
<button id="delete-event" class="owner-btn ui-btn"
|
||||
type="submit" name="_method" value="DELETE">Delete</button>
|
||||
</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
<p class="show">{{ event.description }}</p>
|
||||
<span class="edit hidden">
|
||||
<textarea name="description" data-provide="markdown">{{ event.description }}</textarea>
|
||||
</span>
|
||||
|
||||
<div class="clear"></div>
|
||||
</div></span>
|
||||
<div class="clear"></div>
|
||||
</div></span>
|
||||
</div>
|
||||
<div id="bottom-stripe"></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</form>
|
||||
{% endblock %}
|
||||
|
|
|
@ -2,11 +2,18 @@
|
|||
{% include 'macros.html' %}
|
||||
|
||||
{% block title %}{% endblock %}
|
||||
|
||||
{% block head %}
|
||||
{{ super() }}
|
||||
<link rel="stylesheet" href="/ext/css/jquery-ui-1.10.3.custom.css" />
|
||||
<link rel="stylesheet" href="/ext/css/jquery.timepicker.css">
|
||||
<link rel="stylesheet" href="/ext/css/bootstrap-markdown.css">
|
||||
<link rel="stylesheet" href="/css/events.css">
|
||||
{% endblock %}
|
||||
|
||||
{% block body %}{% endblock %}
|
||||
{% block body %}
|
||||
{{ gallery() }}
|
||||
{% endblock %}
|
||||
|
||||
{% block require_main %}/js/events/index{% endblock %}
|
||||
{% block scripts %}
|
||||
|
|
|
@ -1,21 +1,15 @@
|
|||
{% extends "events/layout.html" %}
|
||||
|
||||
{% block title %}Event Map{% endblock %}
|
||||
{% block head %}
|
||||
{{ super() }}
|
||||
<link rel="stylesheet" href="/ext/css/jquery-ui-1.10.3.custom.css" />
|
||||
<link rel="stylesheet" href="/ext/css/jquery.timepicker.css">
|
||||
<link rel="stylesheet" href="/ext/css/bootstrap-markdown.css">
|
||||
{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
|
||||
{{ super() }}
|
||||
<div class="overlay-container">
|
||||
<div class="map-overlay">
|
||||
|
||||
<form id="find-event" action="/events" method="GET">
|
||||
<h2><span class="icon-search"></span> Find an event</h2>
|
||||
<input name="_csrf" type="hidden" value="{{ csrf_token }}" />
|
||||
<input name="_csrf" type="hidden" value="{{ csrf }}" />
|
||||
<input name="find-where" type="text" placeholder="Where?" />
|
||||
<input name="find-when" type="text" placeholder="When?" class="datepicker" />
|
||||
<button type="submit"><span class="icon-chevron-right"></span></button>
|
||||
|
@ -25,7 +19,7 @@
|
|||
<h2 class='formExpandButton'><span class="icon-plus"></span> Add an event</h2>
|
||||
</div>
|
||||
|
||||
<form id="create-event" action="/events" method="POST" enctype="multipart/form-data" class='hidden'>
|
||||
<form id="create-event" action="/events" method="POST" class='hidden'>
|
||||
<h2 class='formExpandButton'><span class="icon-remove"></span> Add an event</h2>
|
||||
<fieldset>
|
||||
<label for="title"> Name your event </label>
|
||||
|
@ -86,7 +80,7 @@
|
|||
<input name="picture" type="hidden" />
|
||||
<div id="image-upload">Upload an image</div>
|
||||
</div>
|
||||
<input name="_csrf" type="hidden" value="{{ csrf_token }}" />
|
||||
<input name="_csrf" type="hidden" value="{{ csrf }}" />
|
||||
|
||||
<button type="submit"><span class="icon-plus"></span> Submit your event</button>
|
||||
</fieldset>
|
||||
|
|
Загрузка…
Ссылка в новой задаче