metamaps--metamaps/frontend/src/Metamaps/Map/InfoBox.js
2016-11-07 15:25:08 -05:00

402 lines
14 KiB
JavaScript

/* global $, Hogan, Bloodhound, Countable */
import outdent from 'outdent'
import Active from '../Active'
import DataModel from '../DataModel'
import GlobalUI from '../GlobalUI'
import Router from '../Router'
import Util from '../Util'
const InfoBox = {
isOpen: false,
changing: false,
selectingPermission: false,
changePermissionText: "<div class='tooltips'>As the creator, you can change the permission of this map, and the permission of all the topics and synapses you have authority to change will change as well.</div>",
nameHTML: outdent`
<span class="best_in_place best_in_place_name"
id="best_in_place_map_{{id}}_name"
data-bip-url="/maps/{{id}}"
data-bip-object="map"
data-bip-attribute="name"
data-bip-type="textarea"
data-bip-activator="#mapInfoName"
data-bip-value="{{name}}"
>{{name}}</span>`,
descHTML: outdent`
<span class="best_in_place best_in_place_desc"
id="best_in_place_map_{{id}}_desc"
data-bip-url="/maps/{{id}}"
data-bip-object="map"
data-bip-attribute="desc"
data-bip-nil="Click to add description..."
data-bip-type="textarea"
data-bip-activator="#mapInfoDesc"
data-bip-value="{{desc}}"
>{{desc}}</span>`,
userImageUrl: '',
init: function(serverData) {
var self = InfoBox
$('.mapInfoIcon').click(self.toggleBox)
$('.mapInfoBox').click(function(event) {
event.stopPropagation()
})
$('body').click(self.close)
self.attachEventListeners()
self.generateBoxHTML = Hogan.compile($('#mapInfoBoxTemplate').html())
self.userImageUrl = serverData['user.png']
var querystring = window.location.search.replace(/^\?/, '')
if (querystring === 'new') {
self.open()
$('.mapInfoBox').addClass('mapRequestTitle')
$('#mapInfoName').trigger('click')
$('#mapInfoName textarea').focus()
$('#mapInfoName textarea').select()
}
},
toggleBox: function(event) {
var self = InfoBox
if (self.isOpen) self.close()
else self.open()
event.stopPropagation()
},
open: function() {
var self = InfoBox
$('.mapInfoIcon div').addClass('hide')
if (!self.isOpen && !self.changing) {
self.changing = true
$('.mapInfoBox').fadeIn(200, function() {
self.changing = false
self.isOpen = true
})
}
},
close: function() {
var self = InfoBox
$('.mapInfoIcon div').removeClass('hide')
if (!self.changing) {
self.changing = true
$('.mapInfoBox').fadeOut(200, function() {
self.changing = false
self.isOpen = false
self.hidePermissionSelect()
$('.mapContributors .tip').hide()
})
}
},
load: function() {
var self = InfoBox
var map = Active.Map
var obj = map.pick('permission', 'topic_count', 'synapse_count')
var isCreator = map.authorizePermissionChange(Active.Mapper)
var canEdit = map.authorizeToEdit(Active.Mapper)
var relevantPeople = map.get('permission') === 'commons' ? DataModel.Mappers : DataModel.Collaborators
var shareable = map.get('permission') !== 'private'
obj['name'] = canEdit ? Hogan.compile(self.nameHTML).render({id: map.id, name: map.get('name')}) : map.get('name')
obj['desc'] = canEdit ? Hogan.compile(self.descHTML).render({id: map.id, desc: map.get('desc')}) : map.get('desc')
obj['map_creator_tip'] = isCreator ? self.changePermissionText : ''
obj['contributor_count'] = relevantPeople.length
obj['contributors_class'] = relevantPeople.length > 1 ? 'multiple' : ''
obj['contributors_class'] += relevantPeople.length === 2 ? ' mTwo' : ''
obj['contributor_image'] = relevantPeople.length > 0 ? relevantPeople.models[0].get('image') : self.userImageUrl
obj['contributor_list'] = self.createContributorList()
obj['user_name'] = isCreator ? 'You' : map.get('user_name')
obj['created_at'] = map.get('created_at_clean')
obj['updated_at'] = map.get('updated_at_clean')
var classes = isCreator ? 'yourMap' : ''
classes += canEdit ? ' canEdit' : ''
classes += shareable ? ' shareable' : ''
$('.mapInfoBox').removeClass('shareable yourMap canEdit')
.addClass(classes)
.html(self.generateBoxHTML.render(obj))
self.attachEventListeners()
},
attachEventListeners: function() {
var self = InfoBox
$('.mapInfoBox.canEdit .best_in_place').best_in_place()
// because anyone who can edit the map can change the map title
var bipName = $('.mapInfoBox .best_in_place_name')
bipName.unbind('best_in_place:activate').bind('best_in_place:activate', function() {
var $el = bipName.find('textarea')
var el = $el[0]
$el.attr('maxlength', '140')
$('.mapInfoName').append('<div class="nameCounter forMap"></div>')
var callback = function(data) {
$('.nameCounter.forMap').html(data.all + '/140')
}
Countable.live(el, callback)
})
bipName.unbind('best_in_place:deactivate').bind('best_in_place:deactivate', function() {
$('.nameCounter.forMap').remove()
})
$('.mapInfoName .best_in_place_name').unbind('ajax:success').bind('ajax:success', function() {
var name = $(this).html()
Active.Map.set('name', name)
Active.Map.trigger('saved')
// mobile menu
$('#header_content').html(name)
$('.mapInfoBox').removeClass('mapRequestTitle')
document.title = `${name} | Metamaps`
window.history.replaceState('', `${name} | Metamaps`, window.location.pathname)
})
$('.mapInfoDesc .best_in_place_desc').unbind('ajax:success').bind('ajax:success', function() {
var desc = $(this).html()
Active.Map.set('desc', desc)
Active.Map.trigger('saved')
})
$('.mapInfoDesc .best_in_place_desc, .mapInfoName .best_in_place_name').unbind('keypress').keypress(function(e) {
const ENTER = 13
if (e.which === ENTER) {
$(this).data('bestInPlaceEditor').update()
}
})
$('.yourMap .mapPermission').unbind().click(self.onPermissionClick)
// .yourMap in the unbind/bind is just a namespace for the events
// not a reference to the class .yourMap on the .mapInfoBox
$('.mapInfoBox.yourMap').unbind('.yourMap').bind('click.yourMap', self.hidePermissionSelect)
$('.yourMap .mapInfoDelete').unbind().click(self.deleteActiveMap)
$('.mapContributors span, #mapContribs').unbind().click(function(event) {
$('.mapContributors .tip').toggle()
event.stopPropagation()
})
$('.mapContributors .tip').unbind().click(function(event) {
event.stopPropagation()
})
$('.mapContributors .tip li a').click(Router.intercept)
$('.mapInfoBox').unbind('.hideTip').bind('click.hideTip', function() {
$('.mapContributors .tip').hide()
})
self.addTypeahead()
},
addTypeahead: function() {
var self = InfoBox
if (!Active.Map) return
// for autocomplete
var collaborators = {
name: 'collaborators',
limit: 9999,
display: function(s) { return s.label },
templates: {
notFound: function(s) {
return Hogan.compile($('#collaboratorSearchTemplate').html()).render({
value: 'No results',
label: 'No results',
rtype: 'noresult',
profile: self.userImageUrl
})
},
suggestion: function(s) {
return Hogan.compile($('#collaboratorSearchTemplate').html()).render(s)
}
},
source: new Bloodhound({
datumTokenizer: Bloodhound.tokenizers.obj.whitespace('value'),
queryTokenizer: Bloodhound.tokenizers.whitespace,
remote: {
url: '/search/mappers?term=%QUERY',
wildcard: '%QUERY'
}
})
}
// for adding map collaborators, who will have edit rights
if (Active.Mapper && Active.Mapper.id === Active.Map.get('user_id')) {
$('.collaboratorSearchField').typeahead(
{
highlight: false
},
[collaborators]
)
$('.collaboratorSearchField').bind('typeahead:select', self.handleResultClick)
$('.mapContributors .removeCollaborator').click(function() {
self.removeCollaborator(parseInt($(this).data('id')))
})
}
},
removeCollaborator: function(collaboratorId) {
var self = InfoBox
DataModel.Collaborators.remove(DataModel.Collaborators.get(collaboratorId))
var mapperIds = DataModel.Collaborators.models.map(function(mapper) { return mapper.id })
$.post('/maps/' + Active.Map.id + '/access', { access: mapperIds })
self.updateNumbers()
},
addCollaborator: function(newCollaboratorId) {
var self = InfoBox
if (DataModel.Collaborators.get(newCollaboratorId)) {
GlobalUI.notifyUser('That user already has access')
return
}
function callback(mapper) {
DataModel.Collaborators.add(mapper)
var mapperIds = DataModel.Collaborators.models.map(function(mapper) { return mapper.id })
$.post('/maps/' + Active.Map.id + '/access', { access: mapperIds })
var name = DataModel.Collaborators.get(newCollaboratorId).get('name')
GlobalUI.notifyUser(name + ' will be notified by email')
self.updateNumbers()
}
$.getJSON('/users/' + newCollaboratorId + '.json', callback)
},
handleResultClick: function(event, item) {
var self = InfoBox
self.addCollaborator(item.id)
$('.collaboratorSearchField').typeahead('val', '')
},
updateNameDescPerm: function(name, desc, perm) {
$('.mapInfoBox').removeClass('mapRequestTitle')
$('.mapInfoName .best_in_place_name').html(name)
$('.mapInfoDesc .best_in_place_desc').html(desc)
$('.mapInfoBox .mapPermission').removeClass('commons public private').addClass(perm)
},
createContributorList: function() {
var relevantPeople = Active.Map.get('permission') === 'commons' ? DataModel.Mappers : DataModel.Collaborators
var activeMapperIsCreator = Active.Mapper && Active.Mapper.id === Active.Map.get('user_id')
var string = ''
string += '<ul>'
relevantPeople.each(function(m) {
var isCreator = Active.Map.get('user_id') === m.get('id')
string += '<li><a href="/explore/mapper/' + m.get('id') + '">' + '<img class="rtUserImage" width="25" height="25" src="' + m.get('image') + '" />' + m.get('name')
if (isCreator) string += ' (creator)'
string += '</a>'
if (activeMapperIsCreator && !isCreator) string += '<span class="removeCollaborator" data-id="' + m.get('id') + '"></span>'
string += '</li>'
})
string += '</ul>'
if (activeMapperIsCreator) {
string += '<div class="collabSearchField"><span class="addCollab"></span><input class="collaboratorSearchField" placeholder="Add a collaborator!"></input></div>'
}
return string
},
updateNumbers: function() {
if (!Active.Map) return
const self = InfoBox
var relevantPeople = Active.Map.get('permission') === 'commons' ? DataModel.Mappers : DataModel.Collaborators
let contributorsClass = ''
if (relevantPeople.length === 2) {
contributorsClass = 'multiple mTwo'
} else if (relevantPeople.length > 2) {
contributorsClass = 'multiple'
}
let contributorsImage = self.userImageUrl
if (relevantPeople.length > 0) {
// get the first contributor and use their image
contributorsImage = relevantPeople.models[0].get('image')
}
$('.mapContributors img').attr('src', contributorsImage).removeClass('multiple mTwo').addClass(contributorsClass)
$('.mapContributors span').text(relevantPeople.length)
$('.mapContributors .tip').html(self.createContributorList())
self.addTypeahead()
$('.mapContributors .tip').unbind().click(function(event) {
event.stopPropagation()
})
$('.mapTopics').text(DataModel.Topics.length)
$('.mapSynapses').text(DataModel.Synapses.length)
$('.mapEditedAt').html('<span>Last edited: </span>' + Util.nowDateFormatted())
},
onPermissionClick: function(event) {
var self = InfoBox
if (!self.selectingPermission) {
self.selectingPermission = true
$(this).addClass('minimize') // this line flips the drop down arrow to a pull up arrow
if ($(this).hasClass('commons')) {
$(this).append('<ul class="permissionSelect"><li class="public"></li><li class="private"></li></ul>')
} else if ($(this).hasClass('public')) {
$(this).append('<ul class="permissionSelect"><li class="commons"></li><li class="private"></li></ul>')
} else if ($(this).hasClass('private')) {
$(this).append('<ul class="permissionSelect"><li class="commons"></li><li class="public"></li></ul>')
}
$('.mapPermission .permissionSelect li').click(self.selectPermission)
event.stopPropagation()
}
},
hidePermissionSelect: function() {
var self = InfoBox
self.selectingPermission = false
$('.mapPermission').removeClass('minimize') // this line flips the pull up arrow to a drop down arrow
$('.mapPermission .permissionSelect').remove()
},
selectPermission: function(event) {
var self = InfoBox
self.selectingPermission = false
var permission = $(this).attr('class')
Active.Map.save({
permission: permission
})
Active.Map.updateMapWrapper()
const shareable = permission === 'private' ? '' : 'shareable'
$('.mapPermission').removeClass('commons public private minimize').addClass(permission)
$('.mapPermission .permissionSelect').remove()
$('.mapInfoBox').removeClass('shareable').addClass(shareable)
event.stopPropagation()
},
deleteActiveMap: function() {
var confirmString = 'Are you sure you want to delete this map? '
confirmString += 'This action is irreversible. It will not delete the topics and synapses on the map.'
var doIt = window.confirm(confirmString)
var map = Active.Map
var mapper = Active.Mapper
var authorized = map.authorizePermissionChange(mapper)
if (doIt && authorized) {
InfoBox.close()
DataModel.Maps.Active.remove(map)
DataModel.Maps.Featured.remove(map)
DataModel.Maps.Mine.remove(map)
DataModel.Maps.Shared.remove(map)
map.destroy()
Router.home()
GlobalUI.notifyUser('Map eliminated!')
} else if (!authorized) {
window.alert("Hey now. We can't just go around willy nilly deleting other people's maps now can we? Run off and find something constructive to do, eh?")
}
}
}
export default InfoBox