diff --git a/frontend/src/Metamaps/GlobalUI.js b/frontend/src/Metamaps/GlobalUI.js deleted file mode 100644 index 7af133de..00000000 --- a/frontend/src/Metamaps/GlobalUI.js +++ /dev/null @@ -1,646 +0,0 @@ -/* global Metamaps, $, Hogan, Bloodhound */ -import Active from './Active' -import Create from './Create' -import Filter from './Filter' -import Router from './Router' - -/* - * Metamaps.Backbone - * Metamaps.Erb - * Metamaps.Maps - */ - -const GlobalUI = { - notifyTimeout: null, - lightbox: null, - init: function () { - var self = GlobalUI; - - self.Search.init(); - self.CreateMap.init(); - self.Account.init(); - - if ($('#toast').html().trim()) self.notifyUser($('#toast').html()) - - //bind lightbox clicks - $('.openLightbox').click(function (event) { - self.openLightbox($(this).attr('data-open')); - event.preventDefault(); - return false; - }); - - $('#lightbox_screen, #lightbox_close').click(self.closeLightbox); - - // initialize global backbone models and collections - if (Active.Mapper) Active.Mapper = new Metamaps.Backbone.Mapper(Active.Mapper); - - var myCollection = Metamaps.Maps.Mine ? Metamaps.Maps.Mine : []; - var sharedCollection = Metamaps.Maps.Shared ? Metamaps.Maps.Shared : []; - var starredCollection = Metamaps.Maps.Starred ? Metamaps.Maps.Starred : []; - var mapperCollection = []; - var mapperOptionsObj = {id: 'mapper', sortBy: 'updated_at' }; - if (Metamaps.Maps.Mapper) { - mapperCollection = Metamaps.Maps.Mapper.models; - mapperOptionsObj.mapperId = Metamaps.Maps.Mapper.id; - } - var featuredCollection = Metamaps.Maps.Featured ? Metamaps.Maps.Featured : []; - var activeCollection = Metamaps.Maps.Active ? Metamaps.Maps.Active : []; - Metamaps.Maps.Mine = new Metamaps.Backbone.MapsCollection(myCollection, {id: 'mine', sortBy: 'updated_at' }); - Metamaps.Maps.Shared = new Metamaps.Backbone.MapsCollection(sharedCollection, {id: 'shared', sortBy: 'updated_at' }); - Metamaps.Maps.Starred = new Metamaps.Backbone.MapsCollection(starredCollection, {id: 'starred', sortBy: 'updated_at' }); - // 'Mapper' refers to another mapper - Metamaps.Maps.Mapper = new Metamaps.Backbone.MapsCollection(mapperCollection, mapperOptionsObj); - Metamaps.Maps.Featured = new Metamaps.Backbone.MapsCollection(featuredCollection, {id: 'featured', sortBy: 'updated_at' }); - Metamaps.Maps.Active = new Metamaps.Backbone.MapsCollection(activeCollection, {id: 'active', sortBy: 'updated_at' }); - }, - showDiv: function (selector) { - $(selector).show() - $(selector).animate({ - opacity: 1 - }, 200, 'easeOutCubic') - }, - hideDiv: function (selector) { - $(selector).animate({ - opacity: 0 - }, 200, 'easeInCubic', function () { $(this).hide() }) - }, - openLightbox: function (which) { - var self = GlobalUI; - - $('.lightboxContent').hide(); - $('#' + which).show(); - - self.lightbox = which; - - $('#lightbox_overlay').show(); - - var heightOfContent = '-' + ($('#lightbox_main').height() / 2) + 'px'; - // animate the content in from the bottom - $('#lightbox_main').animate({ - 'top': '50%', - 'margin-top': heightOfContent - }, 200, 'easeOutCubic'); - - // fade the black overlay in - $('#lightbox_screen').animate({ - 'opacity': '0.42' - }, 200); - - if (which == "switchMetacodes") { - Create.isSwitchingSet = true; - } - }, - - closeLightbox: function (event) { - var self = GlobalUI; - - if (event) event.preventDefault(); - - // animate the lightbox content offscreen - $('#lightbox_main').animate({ - 'top': '100%', - 'margin-top': '0' - }, 200, 'easeInCubic'); - - // fade the black overlay out - $('#lightbox_screen').animate({ - 'opacity': '0.0' - }, 200, function () { - $('#lightbox_overlay').hide(); - }); - - if (self.lightbox === 'forkmap') GlobalUI.CreateMap.reset('fork_map'); - if (self.lightbox === 'newmap') GlobalUI.CreateMap.reset('new_map'); - if (Create && Create.isSwitchingSet) { - Create.cancelMetacodeSetSwitch(); - } - self.lightbox = null; - }, - notifyUser: function (message, leaveOpen) { - var self = GlobalUI; - - $('#toast').html(message) - self.showDiv('#toast') - clearTimeout(self.notifyTimeOut); - if (!leaveOpen) { - self.notifyTimeOut = setTimeout(function () { - self.hideDiv('#toast') - }, 8000); - } - }, - clearNotify: function() { - var self = GlobalUI; - - clearTimeout(self.notifyTimeOut); - self.hideDiv('#toast') - }, - shareInvite: function(inviteLink) { - window.prompt("To copy the invite link, press: Ctrl+C, Enter", inviteLink); - } -} - -GlobalUI.CreateMap = { - newMap: null, - emptyMapForm: "", - emptyForkMapForm: "", - topicsToMap: [], - synapsesToMap: [], - init: function () { - var self = GlobalUI.CreateMap; - - self.newMap = new Metamaps.Backbone.Map({ permission: 'commons' }); - - self.bindFormEvents(); - - self.emptyMapForm = $('#new_map').html(); - - }, - bindFormEvents: function () { - var self = GlobalUI.CreateMap; - - $('.new_map input, .new_map div').unbind('keypress').bind('keypress', function(event) { - if (event.keyCode === 13) self.submit() - }) - - $('.new_map button.cancel').unbind().bind('click', function (event) { - event.preventDefault(); - GlobalUI.closeLightbox(); - }); - $('.new_map button.submitMap').unbind().bind('click', self.submit); - - // bind permission changer events on the createMap form - $('.permIcon').unbind().bind('click', self.switchPermission); - }, - closeSuccess: function () { - $('#mapCreatedSuccess').fadeOut(300, function(){ - $(this).remove(); - }); - }, - generateSuccessMessage: function (id) { - var stringStart = "
SUCCESS!
Your map has been created. Do you want to: Go to your new map"; - stringStart += "ORStay on this "; - var page = Active.Map ? 'map' : 'page'; - var stringEnd = "
"; - return stringStart + page + stringEnd; - }, - switchPermission: function () { - var self = GlobalUI.CreateMap; - - self.newMap.set('permission', $(this).attr('data-permission')); - $(this).siblings('.permIcon').find('.mapPermIcon').removeClass('selected'); - $(this).find('.mapPermIcon').addClass('selected'); - - var permText = $(this).find('.tip').html(); - $(this).parents('.new_map').find('.permText').html(permText); - }, - submit: function (event) { - if (event) event.preventDefault(); - - var self = GlobalUI.CreateMap; - - if (GlobalUI.lightbox === 'forkmap') { - self.newMap.set('topicsToMap', self.topicsToMap); - self.newMap.set('synapsesToMap', self.synapsesToMap); - } - - var formId = GlobalUI.lightbox === 'forkmap' ? '#fork_map' : '#new_map'; - var $form = $(formId); - - self.newMap.set('name', $form.find('#map_name').val()); - self.newMap.set('desc', $form.find('#map_desc').val()); - - if (self.newMap.get('name').length===0){ - self.throwMapNameError(); - return; - } - - self.newMap.save(null, { - success: self.success - // TODO add error message - }); - - GlobalUI.closeLightbox(); - GlobalUI.notifyUser('Working...'); - }, - throwMapNameError: function () { - var self = GlobalUI.CreateMap; - - var formId = GlobalUI.lightbox === 'forkmap' ? '#fork_map' : '#new_map'; - var $form = $(formId); - - var message = $("
Please enter a map name...
"); - - $form.find('#map_name').after(message); - setTimeout(function(){ - message.fadeOut('fast', function(){ - message.remove(); - }); - }, 5000); - }, - success: function (model) { - var self = GlobalUI.CreateMap; - - //push the new map onto the collection of 'my maps' - Metamaps.Maps.Mine.add(model); - - var formId = GlobalUI.lightbox === 'forkmap' ? '#fork_map' : '#new_map'; - var form = $(formId); - - GlobalUI.clearNotify(); - $('#wrapper').append(self.generateSuccessMessage(model.id)); - - }, - reset: function (id) { - var self = GlobalUI.CreateMap; - - var form = $('#' + id); - - if (id === "fork_map") { - self.topicsToMap = []; - self.synapsesToMap = []; - form.html(self.emptyForkMapForm); - } - else { - form.html(self.emptyMapForm); - } - - self.bindFormEvents(); - self.newMap = new Metamaps.Backbone.Map({ permission: 'commons' }); - - return false; - }, -} - -GlobalUI.Account = { - isOpen: false, - changing: false, - init: function () { - var self = GlobalUI.Account; - - $('.sidebarAccountIcon').click(self.toggleBox); - $('.sidebarAccountBox').click(function(event){ - event.stopPropagation(); - }); - $('body').click(self.close); - }, - toggleBox: function (event) { - var self = GlobalUI.Account; - - if (self.isOpen) self.close(); - else self.open(); - - event.stopPropagation(); - }, - open: function () { - var self = GlobalUI.Account; - - Filter.close(); - $('.sidebarAccountIcon .tooltipsUnder').addClass('hide'); - - - if (!self.isOpen && !self.changing) { - self.changing = true; - $('.sidebarAccountBox').fadeIn(200, function () { - self.changing = false; - self.isOpen = true; - $('.sidebarAccountBox #user_email').focus(); - }); - } - }, - close: function () { - var self = GlobalUI.Account; - - $('.sidebarAccountIcon .tooltipsUnder').removeClass('hide'); - if (!self.changing) { - self.changing = true; - $('.sidebarAccountBox #user_email').blur(); - $('.sidebarAccountBox').fadeOut(200, function () { - self.changing = false; - self.isOpen = false; - }); - } - } -} - -GlobalUI.Search = { - locked: false, - isOpen: false, - limitTopicsToMe: false, - limitMapsToMe: false, - timeOut: null, - changing: false, - optionsInitialized: false, - init: function () { - var self = GlobalUI.Search; - - var loader = new CanvasLoader('searchLoading'); - loader.setColor('#4fb5c0'); // default is '#000000' - loader.setDiameter(24); // default is 40 - loader.setDensity(41); // default is 40 - loader.setRange(0.9); // default is 1.3 - loader.show(); // Hidden by default - - // bind the hover events - $(".sidebarSearch").hover(function () { - self.open() - }, function () { - self.close(800, false) - }); - - $('.sidebarSearchIcon').click(function (e) { - $('.sidebarSearchField').focus(); - }); - $('.sidebarSearch').click(function (e) { - e.stopPropagation(); - }); - $('body').click(function (e) { - self.close(0, false); - }); - - // open if the search is closed and user hits ctrl+/ - // close if they hit ESC - $('body').bind('keyup', function (e) { - switch (e.which) { - case 191: - if ((e.ctrlKey && !self.isOpen) || (e.ctrlKey && self.locked)) { - self.open(true); // true for focus - } - break; - case 27: - if (self.isOpen) { - self.close(0, true); - } - break; - - default: - break; //console.log(e.which); - } - }); - - self.startTypeahead(); - }, - lock: function() { - var self = GlobalUI.Search; - self.locked = true; - }, - unlock: function() { - var self = GlobalUI.Search; - self.locked = false; - }, - open: function (focus) { - var self = GlobalUI.Search; - - clearTimeout(self.timeOut); - if (!self.isOpen && !self.changing && !self.locked) { - self.changing = true; - $('.sidebarSearch .twitter-typeahead, .sidebarSearch .tt-hint, .sidebarSearchField').animate({ - width: '400px' - }, 300, function () { - if (focus) $('.sidebarSearchField').focus(); - $('.sidebarSearchField, .sidebarSearch .tt-hint').css({ - padding: '7px 10px 3px 10px', - width: '380px' - }); - self.changing = false; - self.isOpen = true; - }); - } - }, - close: function (closeAfter, bypass) { - // for now - return - - var self = GlobalUI.Search; - - self.timeOut = setTimeout(function () { - if (!self.locked && !self.changing && self.isOpen && (bypass || $('.sidebarSearchField.tt-input').val() == '')) { - self.changing = true; - $('.sidebarSearchField, .sidebarSearch .tt-hint').css({ - padding: '7px 0 3px 0', - width: '400px' - }); - $('.sidebarSearch .twitter-typeahead, .sidebarSearch .tt-hint, .sidebarSearchField').animate({ - width: '0' - }, 300, function () { - $('.sidebarSearchField').typeahead('val', ''); - $('.sidebarSearchField').blur(); - self.changing = false; - self.isOpen = false; - }); - } - }, closeAfter); - }, - startTypeahead: function () { - var self = GlobalUI.Search; - - var mapheader = Active.Mapper ? '

Maps

' : '

Maps

'; - var topicheader = Active.Mapper ? '

Topics

' : '

Topics

'; - var mapperheader = '

Mappers

'; - - var topics = { - name: 'topics', - limit: 9999, - - display: function(s) { return s.label; }, - templates: { - notFound: function(s) { - return Hogan.compile(topicheader + $('#topicSearchTemplate').html()).render({ - value: "No results", - label: "No results", - typeImageURL: Metamaps.Erb['icons/wildcard.png'], - rtype: "noresult" - }); - }, - header: topicheader, - suggestion: function(s) { - return Hogan.compile($('#topicSearchTemplate').html()).render(s); - }, - }, - source: new Bloodhound({ - datumTokenizer: Bloodhound.tokenizers.obj.whitespace('value'), - queryTokenizer: Bloodhound.tokenizers.whitespace, - remote: { - url: '/search/topics', - prepare: function(query, settings) { - settings.url += '?term=' + query; - if (Active.Mapper && self.limitTopicsToMe) { - settings.url += "&user=" + Active.Mapper.id.toString(); - } - return settings; - }, - }, - }), - }; - - var maps = { - name: 'maps', - limit: 9999, - display: function(s) { return s.label; }, - templates: { - notFound: function(s) { - return Hogan.compile(mapheader + $('#mapSearchTemplate').html()).render({ - value: "No results", - label: "No results", - rtype: "noresult" - }); - }, - header: mapheader, - suggestion: function(s) { - return Hogan.compile($('#mapSearchTemplate').html()).render(s); - }, - }, - source: new Bloodhound({ - datumTokenizer: Bloodhound.tokenizers.obj.whitespace('value'), - queryTokenizer: Bloodhound.tokenizers.whitespace, - remote: { - url: '/search/maps', - prepare: function(query, settings) { - settings.url += '?term=' + query; - if (Active.Mapper && self.limitMapsToMe) { - settings.url += "&user=" + Active.Mapper.id.toString(); - } - return settings; - }, - }, - }), - }; - - var mappers = { - name: 'mappers', - limit: 9999, - display: function(s) { return s.label; }, - templates: { - notFound: function(s) { - return Hogan.compile(mapperheader + $('#mapperSearchTemplate').html()).render({ - value: "No results", - label: "No results", - rtype: "noresult", - profile: Metamaps.Erb['user.png'] - }); - }, - header: mapperheader, - suggestion: function(s) { - return Hogan.compile($('#mapperSearchTemplate').html()).render(s); - }, - }, - source: new Bloodhound({ - datumTokenizer: Bloodhound.tokenizers.obj.whitespace('value'), - queryTokenizer: Bloodhound.tokenizers.whitespace, - remote: { - url: '/search/mappers?term=%QUERY', - wildcard: '%QUERY', - }, - }), - }; - - // Take all that crazy setup data and put it together into one beautiful typeahead call! - $('.sidebarSearchField').typeahead( - { - highlight: true, - }, - [topics, maps, mappers] - ); - - //Set max height of the search results box to prevent it from covering bottom left footer - $('.sidebarSearchField').bind('typeahead:render', function (event) { - self.initSearchOptions(); - self.hideLoader(); - var h = $(window).height(); - $(".tt-dropdown-menu").css('max-height', h - 100); - if (self.limitTopicsToMe) { - $('#limitTopicsToMe').prop('checked', true); - } - if (self.limitMapsToMe) { - $('#limitMapsToMe').prop('checked', true); - } - }); - $(window).resize(function () { - var h = $(window).height(); - $(".tt-dropdown-menu").css('max-height', h - 100); - }); - - // tell the autocomplete to launch a new tab with the topic, map, or mapper you clicked on - $('.sidebarSearchField').bind('typeahead:select', self.handleResultClick); - - // don't do it, if they clicked on a 'addToMap' button - $('.sidebarSearch button.addToMap').click(function (event) { - event.stopPropagation(); - }); - - // make sure that when you click on 'limit to me' or 'toggle section' it works - $('.sidebarSearchField.tt-input').keyup(function(){ - if ($('.sidebarSearchField.tt-input').val() === '') { - self.hideLoader(); - } else { - self.showLoader(); - } - }); - - }, - handleResultClick: function (event, datum, dataset) { - var self = GlobalUI.Search; - - self.hideLoader(); - - if (["topic", "map", "mapper"].indexOf(datum.rtype) !== -1) { - self.close(0, true); - if (datum.rtype == "topic") { - Router.topics(datum.id); - } else if (datum.rtype == "map") { - Router.maps(datum.id); - } else if (datum.rtype == "mapper") { - Router.explore("mapper", datum.id); - } - } - }, - initSearchOptions: function () { - var self = GlobalUI.Search; - - function toggleResultSet(set) { - var s = $('.tt-dataset-' + set + ' .tt-suggestion, .tt-dataset-' + set + ' .resultnoresult'); - if (s.is(':visible')) { - s.hide(); - $(this).removeClass('minimizeResults').addClass('maximizeResults'); - } else { - s.show(); - $(this).removeClass('maximizeResults').addClass('minimizeResults'); - } - } - - $('.limitToMe').unbind().bind("change", function (e) { - if ($(this).attr('id') == 'limitTopicsToMe') { - self.limitTopicsToMe = !self.limitTopicsToMe; - } - if ($(this).attr('id') == 'limitMapsToMe') { - self.limitMapsToMe = !self.limitMapsToMe; - } - - // set the value of the search equal to itself to retrigger the - // autocomplete event - var searchQuery = $('.sidebarSearchField.tt-input').val(); - $(".sidebarSearchField").typeahead('val', '') - .typeahead('val', searchQuery); - }); - - // when the user clicks minimize section, hide the results for that section - $('.minimizeMapperResults').unbind().click(function (e) { - toggleResultSet.call(this, 'mappers'); - }); - $('.minimizeTopicResults').unbind().click(function (e) { - toggleResultSet.call(this, 'topics'); - }); - $('.minimizeMapResults').unbind().click(function (e) { - toggleResultSet.call(this, 'maps'); - }); - }, - hideLoader: function () { - $('#searchLoading').hide(); - }, - showLoader: function () { - $('#searchLoading').show(); - } -} - -export default GlobalUI diff --git a/frontend/src/Metamaps/GlobalUI/Account.js b/frontend/src/Metamaps/GlobalUI/Account.js new file mode 100644 index 00000000..210627ff --- /dev/null +++ b/frontend/src/Metamaps/GlobalUI/Account.js @@ -0,0 +1,55 @@ +/* global $ */ + +import Filter from '../Filter' + +const Account = { + isOpen: false, + changing: false, + init: function () { + var self = Account + + $('.sidebarAccountIcon').click(self.toggleBox) + $('.sidebarAccountBox').click(function (event) { + event.stopPropagation() + }) + $('body').click(self.close) + }, + toggleBox: function (event) { + var self = Account + + if (self.isOpen) self.close() + else self.open() + + event.stopPropagation() + }, + open: function () { + var self = Account + + Filter.close() + $('.sidebarAccountIcon .tooltipsUnder').addClass('hide') + + if (!self.isOpen && !self.changing) { + self.changing = true + $('.sidebarAccountBox').fadeIn(200, function () { + self.changing = false + self.isOpen = true + $('.sidebarAccountBox #user_email').focus() + }) + } + }, + close: function () { + var self = Account + + $('.sidebarAccountIcon .tooltipsUnder').removeClass('hide') + if (!self.changing) { + self.changing = true + $('.sidebarAccountBox #user_email').blur() + $('.sidebarAccountBox').fadeOut(200, function () { + self.changing = false + self.isOpen = false + }) + } + } +} + +export default Account diff --git a/frontend/src/Metamaps/GlobalUI/CreateMap.js b/frontend/src/Metamaps/GlobalUI/CreateMap.js new file mode 100644 index 00000000..a24c73c8 --- /dev/null +++ b/frontend/src/Metamaps/GlobalUI/CreateMap.js @@ -0,0 +1,137 @@ +/* global Metamaps, $ */ + +import Active from '../Active' +import GlobalUI from './index' + +/* + * Metamaps.Backbone + * Metamaps.Maps + */ + +const CreateMap = { + newMap: null, + emptyMapForm: '', + emptyForkMapForm: '', + topicsToMap: [], + synapsesToMap: [], + init: function () { + var self = CreateMap + + self.newMap = new Metamaps.Backbone.Map({ permission: 'commons' }) + + self.bindFormEvents() + + self.emptyMapForm = $('#new_map').html() + }, + bindFormEvents: function () { + var self = CreateMap + + $('.new_map input, .new_map div').unbind('keypress').bind('keypress', function (event) { + if (event.keyCode === 13) self.submit() + }) + + $('.new_map button.cancel').unbind().bind('click', function (event) { + event.preventDefault() + GlobalUI.closeLightbox() + }) + $('.new_map button.submitMap').unbind().bind('click', self.submit) + + // bind permission changer events on the createMap form + $('.permIcon').unbind().bind('click', self.switchPermission) + }, + closeSuccess: function () { + $('#mapCreatedSuccess').fadeOut(300, function () { + $(this).remove() + }) + }, + generateSuccessMessage: function (id) { + var stringStart = "
SUCCESS!
Your map has been created. Do you want to: Go to your new map" + stringStart += "ORStay on this " + var page = Active.Map ? 'map' : 'page' + var stringEnd = '
' + return stringStart + page + stringEnd + }, + switchPermission: function () { + var self = CreateMap + + self.newMap.set('permission', $(this).attr('data-permission')) + $(this).siblings('.permIcon').find('.mapPermIcon').removeClass('selected') + $(this).find('.mapPermIcon').addClass('selected') + + var permText = $(this).find('.tip').html() + $(this).parents('.new_map').find('.permText').html(permText) + }, + submit: function (event) { + if (event) event.preventDefault() + + var self = CreateMap + + if (GlobalUI.lightbox === 'forkmap') { + self.newMap.set('topicsToMap', self.topicsToMap) + self.newMap.set('synapsesToMap', self.synapsesToMap) + } + + var formId = GlobalUI.lightbox === 'forkmap' ? '#fork_map' : '#new_map' + var $form = $(formId) + + self.newMap.set('name', $form.find('#map_name').val()) + self.newMap.set('desc', $form.find('#map_desc').val()) + + if (self.newMap.get('name').length === 0) { + self.throwMapNameError() + return + } + + self.newMap.save(null, { + success: self.success + // TODO add error message + }) + + GlobalUI.closeLightbox() + GlobalUI.notifyUser('Working...') + }, + throwMapNameError: function () { + + var formId = GlobalUI.lightbox === 'forkmap' ? '#fork_map' : '#new_map' + var $form = $(formId) + + var message = $("
Please enter a map name...
") + + $form.find('#map_name').after(message) + setTimeout(function () { + message.fadeOut('fast', function () { + message.remove() + }) + }, 5000) + }, + success: function (model) { + var self = CreateMap + // push the new map onto the collection of 'my maps' + Metamaps.Maps.Mine.add(model) + + GlobalUI.clearNotify() + $('#wrapper').append(self.generateSuccessMessage(model.id)) + }, + reset: function (id) { + var self = CreateMap + + var form = $('#' + id) + + if (id === 'fork_map') { + self.topicsToMap = [] + self.synapsesToMap = [] + form.html(self.emptyForkMapForm) + } else { + form.html(self.emptyMapForm) + } + + self.bindFormEvents() + self.newMap = new Metamaps.Backbone.Map({ permission: 'commons' }) + + return false + } +} + +export default CreateMap diff --git a/frontend/src/Metamaps/GlobalUI/Search.js b/frontend/src/Metamaps/GlobalUI/Search.js new file mode 100644 index 00000000..7ad1fbc1 --- /dev/null +++ b/frontend/src/Metamaps/GlobalUI/Search.js @@ -0,0 +1,251 @@ +/* global Metamaps, $, Hogan, Bloodhound, CanvasLoader */ + +import Active from '../Active' +import Router from '../Router' + +/* + * Metamaps.Erb + * Metamaps.Maps + */ + +const Search = { + locked: false, + isOpen: false, + limitTopicsToMe: false, + limitMapsToMe: false, + changing: false, + optionsInitialized: false, + init: function () { + var self = Search + + // TODO does this overlap with Metamaps.Loading? + // devin sez: I'd like to remove Metamaps.Loading from the rails code + var loader = new CanvasLoader('searchLoading') + loader.setColor('#4fb5c0') // default is '#000000' + loader.setDiameter(24) // default is 40 + loader.setDensity(41) // default is 40 + loader.setRange(0.9) // default is 1.3 + loader.show() // Hidden by default + + $('.sidebarSearchIcon').click(function (e) { + $('.sidebarSearchField').focus() + }) + $('.sidebarSearch').click(function (e) { + e.stopPropagation() + }) + + self.startTypeahead() + }, + focus: function() { + $('.sidebarSearchField').focus() + }, + startTypeahead: function () { + var self = Search + + var mapheader = Active.Mapper ? '

Maps

' : '

Maps

' + var topicheader = Active.Mapper ? '

Topics

' : '

Topics

' + var mapperheader = '

Mappers

' + + var topics = { + name: 'topics', + limit: 9999, + + display: s => s.label, + templates: { + notFound: function (s) { + return Hogan.compile(topicheader + $('#topicSearchTemplate').html()).render({ + value: 'No results', + label: 'No results', + typeImageURL: Metamaps.Erb['icons/wildcard.png'], + rtype: 'noresult' + }) + }, + header: topicheader, + suggestion: function (s) { + return Hogan.compile($('#topicSearchTemplate').html()).render(s) + } + }, + source: new Bloodhound({ + datumTokenizer: Bloodhound.tokenizers.obj.whitespace('value'), + queryTokenizer: Bloodhound.tokenizers.whitespace, + remote: { + url: '/search/topics', + prepare: function (query, settings) { + settings.url += '?term=' + query + if (Active.Mapper && self.limitTopicsToMe) { + settings.url += '&user=' + Active.Mapper.id.toString() + } + return settings + } + } + }) + } + + var maps = { + name: 'maps', + limit: 9999, + display: s => s.label, + templates: { + notFound: function (s) { + return Hogan.compile(mapheader + $('#mapSearchTemplate').html()).render({ + value: 'No results', + label: 'No results', + rtype: 'noresult' + }) + }, + header: mapheader, + suggestion: function (s) { + return Hogan.compile($('#mapSearchTemplate').html()).render(s) + } + }, + source: new Bloodhound({ + datumTokenizer: Bloodhound.tokenizers.obj.whitespace('value'), + queryTokenizer: Bloodhound.tokenizers.whitespace, + remote: { + url: '/search/maps', + prepare: function (query, settings) { + settings.url += '?term=' + query + if (Active.Mapper && self.limitMapsToMe) { + settings.url += '&user=' + Active.Mapper.id.toString() + } + return settings + } + } + }) + } + + var mappers = { + name: 'mappers', + limit: 9999, + display: s => s.label, + templates: { + notFound: function (s) { + return Hogan.compile(mapperheader + $('#mapperSearchTemplate').html()).render({ + value: 'No results', + label: 'No results', + rtype: 'noresult', + profile: Metamaps.Erb['user.png'] + }) + }, + header: mapperheader, + suggestion: function (s) { + return Hogan.compile($('#mapperSearchTemplate').html()).render(s) + } + }, + source: new Bloodhound({ + datumTokenizer: Bloodhound.tokenizers.obj.whitespace('value'), + queryTokenizer: Bloodhound.tokenizers.whitespace, + remote: { + url: '/search/mappers?term=%QUERY', + wildcard: '%QUERY' + } + }) + } + + // Take all that crazy setup data and put it together into one beautiful typeahead call! + $('.sidebarSearchField').typeahead( + { + highlight: true + }, + [topics, maps, mappers] + ) + + // Set max height of the search results box to prevent it from covering bottom left footer + $('.sidebarSearchField').bind('typeahead:render', function (event) { + self.initSearchOptions() + self.hideLoader() + var h = $(window).height() + $('.tt-dropdown-menu').css('max-height', h - 100) + if (self.limitTopicsToMe) { + $('#limitTopicsToMe').prop('checked', true) + } + if (self.limitMapsToMe) { + $('#limitMapsToMe').prop('checked', true) + } + }) + $(window).resize(function () { + var h = $(window).height() + $('.tt-dropdown-menu').css('max-height', h - 100) + }) + + // tell the autocomplete to launch a new tab with the topic, map, or mapper you clicked on + $('.sidebarSearchField').bind('typeahead:select', self.handleResultClick) + + // don't do it, if they clicked on a 'addToMap' button + $('.sidebarSearch button.addToMap').click(function (event) { + event.stopPropagation() + }) + + // make sure that when you click on 'limit to me' or 'toggle section' it works + $('.sidebarSearchField.tt-input').keyup(function () { + if ($('.sidebarSearchField.tt-input').val() === '') { + self.hideLoader() + } else { + self.showLoader() + } + }) + }, + handleResultClick: function (event, datum, dataset) { + var self = Search + + self.hideLoader() + + if (['topic', 'map', 'mapper'].indexOf(datum.rtype) !== -1) { + if (datum.rtype === 'topic') { + Router.topics(datum.id) + } else if (datum.rtype === 'map') { + Router.maps(datum.id) + } else if (datum.rtype === 'mapper') { + Router.explore('mapper', datum.id) + } + } + }, + initSearchOptions: function () { + var self = Search + + function toggleResultSet (set) { + var s = $('.tt-dataset-' + set + ' .tt-suggestion, .tt-dataset-' + set + ' .resultnoresult') + if (s.is(':visible')) { + s.hide() + $(this).removeClass('minimizeResults').addClass('maximizeResults') + } else { + s.show() + $(this).removeClass('maximizeResults').addClass('minimizeResults') + } + } + + $('.limitToMe').unbind().bind('change', function (e) { + if ($(this).attr('id') === 'limitTopicsToMe') { + self.limitTopicsToMe = !self.limitTopicsToMe + } + if ($(this).attr('id') === 'limitMapsToMe') { + self.limitMapsToMe = !self.limitMapsToMe + } + + // set the value of the search equal to itself to retrigger the + // autocomplete event + var searchQuery = $('.sidebarSearchField.tt-input').val() + $('.sidebarSearchField').typeahead('val', '') + .typeahead('val', searchQuery) + }) + + // when the user clicks minimize section, hide the results for that section + $('.minimizeMapperResults').unbind().click(function (e) { + toggleResultSet.call(this, 'mappers') + }) + $('.minimizeTopicResults').unbind().click(function (e) { + toggleResultSet.call(this, 'topics') + }) + $('.minimizeMapResults').unbind().click(function (e) { + toggleResultSet.call(this, 'maps') + }) + }, + hideLoader: function () { + $('#searchLoading').hide() + }, + showLoader: function () { + $('#searchLoading').show() + } +} + +export default Search diff --git a/frontend/src/Metamaps/GlobalUI/index.js b/frontend/src/Metamaps/GlobalUI/index.js new file mode 100644 index 00000000..3b16375e --- /dev/null +++ b/frontend/src/Metamaps/GlobalUI/index.js @@ -0,0 +1,145 @@ +/* global Metamaps, $ */ + +import Active from '../Active' +import Create from '../Create' + +import Search from './Search' +import CreateMap from './CreateMap' +import Account from './Account' + +/* + * Metamaps.Backbone + * Metamaps.Maps + */ + +const GlobalUI = { + notifyTimeout: null, + lightbox: null, + init: function () { + var self = GlobalUI + + self.Search.init() + self.CreateMap.init() + self.Account.init() + + if ($('#toast').html().trim()) self.notifyUser($('#toast').html()) + + // bind lightbox clicks + $('.openLightbox').click(function (event) { + self.openLightbox($(this).attr('data-open')) + event.preventDefault() + return false + }) + + $('#lightbox_screen, #lightbox_close').click(self.closeLightbox) + + // initialize global backbone models and collections + if (Active.Mapper) Active.Mapper = new Metamaps.Backbone.Mapper(Active.Mapper) + + var myCollection = Metamaps.Maps.Mine ? Metamaps.Maps.Mine : [] + var sharedCollection = Metamaps.Maps.Shared ? Metamaps.Maps.Shared : [] + var starredCollection = Metamaps.Maps.Starred ? Metamaps.Maps.Starred : [] + var mapperCollection = [] + var mapperOptionsObj = { id: 'mapper', sortBy: 'updated_at' } + if (Metamaps.Maps.Mapper) { + mapperCollection = Metamaps.Maps.Mapper.models + mapperOptionsObj.mapperId = Metamaps.Maps.Mapper.id + } + var featuredCollection = Metamaps.Maps.Featured ? Metamaps.Maps.Featured : [] + var activeCollection = Metamaps.Maps.Active ? Metamaps.Maps.Active : [] + Metamaps.Maps.Mine = new Metamaps.Backbone.MapsCollection(myCollection, { id: 'mine', sortBy: 'updated_at' }) + Metamaps.Maps.Shared = new Metamaps.Backbone.MapsCollection(sharedCollection, { id: 'shared', sortBy: 'updated_at' }) + Metamaps.Maps.Starred = new Metamaps.Backbone.MapsCollection(starredCollection, { id: 'starred', sortBy: 'updated_at' }) + // 'Mapper' refers to another mapper + Metamaps.Maps.Mapper = new Metamaps.Backbone.MapsCollection(mapperCollection, mapperOptionsObj) + Metamaps.Maps.Featured = new Metamaps.Backbone.MapsCollection(featuredCollection, { id: 'featured', sortBy: 'updated_at' }) + Metamaps.Maps.Active = new Metamaps.Backbone.MapsCollection(activeCollection, { id: 'active', sortBy: 'updated_at' }) + }, + showDiv: function (selector) { + $(selector).show() + $(selector).animate({ + opacity: 1 + }, 200, 'easeOutCubic') + }, + hideDiv: function (selector) { + $(selector).animate({ + opacity: 0 + }, 200, 'easeInCubic', function () { $(this).hide() }) + }, + openLightbox: function (which) { + var self = GlobalUI + + $('.lightboxContent').hide() + $('#' + which).show() + + self.lightbox = which + + $('#lightbox_overlay').show() + + var heightOfContent = '-' + ($('#lightbox_main').height() / 2) + 'px' + // animate the content in from the bottom + $('#lightbox_main').animate({ + 'top': '50%', + 'margin-top': heightOfContent + }, 200, 'easeOutCubic') + + // fade the black overlay in + $('#lightbox_screen').animate({ + 'opacity': '0.42' + }, 200) + + if (which === 'switchMetacodes') { + Create.isSwitchingSet = true + } + }, + + closeLightbox: function (event) { + var self = GlobalUI + + if (event) event.preventDefault() + + // animate the lightbox content offscreen + $('#lightbox_main').animate({ + 'top': '100%', + 'margin-top': '0' + }, 200, 'easeInCubic') + + // fade the black overlay out + $('#lightbox_screen').animate({ + 'opacity': '0.0' + }, 200, function () { + $('#lightbox_overlay').hide() + }) + + if (self.lightbox === 'forkmap') GlobalUI.CreateMap.reset('fork_map') + if (self.lightbox === 'newmap') GlobalUI.CreateMap.reset('new_map') + if (Create && Create.isSwitchingSet) { + Create.cancelMetacodeSetSwitch() + } + self.lightbox = null + }, + notifyUser: function (message, leaveOpen) { + var self = GlobalUI + + $('#toast').html(message) + self.showDiv('#toast') + clearTimeout(self.notifyTimeOut) + if (!leaveOpen) { + self.notifyTimeOut = setTimeout(function () { + self.hideDiv('#toast') + }, 8000) + } + }, + clearNotify: function () { + var self = GlobalUI + + clearTimeout(self.notifyTimeOut) + self.hideDiv('#toast') + }, + shareInvite: function (inviteLink) { + window.prompt('To copy the invite link, press: Ctrl+C, Enter', inviteLink) + } +} + +export { Search, CreateMap, Account } +export default GlobalUI diff --git a/frontend/src/Metamaps/index.js b/frontend/src/Metamaps/index.js index db67409b..d179713e 100644 --- a/frontend/src/Metamaps/index.js +++ b/frontend/src/Metamaps/index.js @@ -9,7 +9,9 @@ import Control from './Control' import Create from './Create' import Debug from './Debug' import Filter from './Filter' -import GlobalUI from './GlobalUI' +import GlobalUI, { + Search, CreateMap, Account as GlobalUI_Account +} from './GlobalUI' import Import from './Import' import JIT from './JIT' import Listeners from './Listeners' @@ -42,6 +44,9 @@ Metamaps.Create = Create Metamaps.Debug = Debug Metamaps.Filter = Filter Metamaps.GlobalUI = GlobalUI +Metamaps.GlobalUI.Search = Search +Metamaps.GlobalUI.CreateMap = CreateMap +Metamaps.GlobalUI.Account = GlobalUI_Account Metamaps.Import = Import Metamaps.JIT = JIT Metamaps.Listeners = Listeners @@ -66,41 +71,38 @@ Metamaps.Util = Util Metamaps.Views = Views Metamaps.Visualize = Visualize -document.addEventListener("DOMContentLoaded", function() { +document.addEventListener('DOMContentLoaded', function () { // initialize all the modules for (const prop in Metamaps) { - // this runs the init function within each sub-object on the Metamaps one - if (Metamaps.hasOwnProperty(prop) && - Metamaps[prop] != null && - Metamaps[prop].hasOwnProperty('init') && - typeof (Metamaps[prop].init) == 'function' - ) { - Metamaps[prop].init() - } + // this runs the init function within each sub-object on the Metamaps one + if (Metamaps.hasOwnProperty(prop) && + Metamaps[prop] != null && + Metamaps[prop].hasOwnProperty('init') && + typeof (Metamaps[prop].init) === 'function' + ) { + Metamaps[prop].init() + } } // load whichever page you are on - if (Metamaps.currentSection === "explore") { - const capitalize = Metamaps.currentPage.charAt(0).toUpperCase() + Metamaps.currentPage.slice(1) + if (Metamaps.currentSection === 'explore') { + const capitalize = Metamaps.currentPage.charAt(0).toUpperCase() + Metamaps.currentPage.slice(1) - Metamaps.Views.ExploreMaps.setCollection( Metamaps.Maps[capitalize] ) - if (Metamaps.currentPage === "mapper") { - Views.ExploreMaps.fetchUserThenRender() - } - else { - Views.ExploreMaps.render() - } - GlobalUI.showDiv('#explore') - } - else if (Metamaps.currentSection === "" && Active.Mapper) { - Views.ExploreMaps.setCollection(Metamaps.Maps.Active) + Metamaps.Views.ExploreMaps.setCollection(Metamaps.Maps[capitalize]) + if (Metamaps.currentPage === 'mapper') { + Views.ExploreMaps.fetchUserThenRender() + } else { Views.ExploreMaps.render() - GlobalUI.showDiv('#explore') - } - else if (Active.Map || Active.Topic) { + } + GlobalUI.showDiv('#explore') + } else if (Metamaps.currentSection === '' && Active.Mapper) { + Views.ExploreMaps.setCollection(Metamaps.Maps.Active) + Views.ExploreMaps.render() + GlobalUI.showDiv('#explore') + } else if (Active.Map || Active.Topic) { Metamaps.Loading.show() JIT.prepareVizData() GlobalUI.showDiv('#infovis') } -}); +}) export default Metamaps