From: Thierry Parmentelat <thierry.parmentelat@inria.fr> Date: Fri, 29 Nov 2013 08:46:33 +0000 (+0100) Subject: the tabs plugin has a persistent_active feature to enable saving the active tab in... X-Git-Tag: myslice-0.3-0~103^2 X-Git-Url: http://git.onelab.eu/?a=commitdiff_plain;h=f19bceaa080ab47bae751cd0597a9fb32d1b6b4f;p=myslice.git the tabs plugin has a persistent_active feature to enable saving the active tab in html5 storage --- diff --git a/plugins/tabs/__init__.py b/plugins/tabs/__init__.py index d1b9bda3..4bab62d9 100644 --- a/plugins/tabs/__init__.py +++ b/plugins/tabs/__init__.py @@ -1,7 +1,24 @@ from unfold.composite import Composite +# although the persistent_active feature originally targetted any kind of +# composite widget, the current implementation clearly +# works only with Tabs, as the javascript function for enabling a given tab +# cannot just set the 'active' class on some DOM element, but is required +# by bootstrap to call the tab() javascript method + class Tabs (Composite): + """A composite plugin for arranging sons as selectable tabs + +persistent_active: if set to True, enables preserving +the domid for the active tab, so that a given page +will re-open with the same active tab +""" + + def __init__ (self, persistent_active=False, *args, **kwds): + Composite.__init__ (self, *args, **kwds) + self.persistent_active=persistent_active + def requirements (self): return { 'js_files' : ['js/tabs.js', 'js/bootstrap.js'], 'css_files' : ['css/bootstrap.css', 'css/tabs.css', ] @@ -10,6 +27,11 @@ class Tabs (Composite): def template_file (self): return "tabs.html" + def template_env (self, request): + inherited=Composite.template_env(self,request) + inherited.update({'persistent_active':self.persistent_active}) + return inherited + # see Composite.py for the details of template_env, that exposes global # 'sons' as a list of sons with each a set of a few attributes def json_settings_list (self): diff --git a/plugins/tabs/static/js/tabs.js b/plugins/tabs/static/js/tabs.js index 75ce452a..bfd568a9 100644 --- a/plugins/tabs/static/js/tabs.js +++ b/plugins/tabs/static/js/tabs.js @@ -1,3 +1,71 @@ +// storing tabs active component in localStorage +// +// based on plugin_helper.js, extended to store the domid of an active tab +// + +var tabs_helper = { + + debug:false, + + ////////// use local storage to remember the active son + // xxx this is a bit fragile in that we assume the elements that need to have the 'active' class + // are direct sons of the .persistent-active elements + // it would be simple to overcome this weakness by requiring the template to set + // .persistent-active-item on these elements active-able instead + store_active_domid : function (domid,active_domid) { + var key='active.'+domid; + if (tabs_helper.debug) messages.debug("storing for domid " + domid + " (key=" + key + ") active_domid=" + active_domid); + $.localStorage.setItem(key,active_domid); + }, + // restore last status + retrieve_last_active_domid : function (domid) { + var key='active.'+domid; + // don't do anything if nothing stored + var retrieved=$.localStorage.getItem(key); + // set default to undefined + if (retrieved==null) retrieved=undefined; + if (tabs_helper.debug) messages.debug ("retrieved active status for " + domid + " (key=" + key + ") -> " + retrieved); + return retrieved; + }, + // tabs_helper.set_active_domid("tabs-resources","tab-resources-list") + set_active_domid : function (domid,active_domid) { + if ( ! active_domid ) return; + if (tabs_helper.debug) messages.debug ("setting active for " + domid + " to active_domid=" + active_domid); + // build something like "#uldomid a[href='#active_domid']" + var selector="#"+domid+" a[href='#"+active_domid+"']"; + console.log("selector="+selector); + $(selector).tab('show'); + }, + set_from_saved_active_domid : function (domid) { + var active_domid=tabs_helper.retrieve_last_active_domid (domid); + if (tabs_helper.debug) messages.debug("restoring active domid for domid " + domid + " -> " + active_domid); + tabs_helper.set_active_domid (domid,active_domid); + }, + + init_all_tabs : function () { + ////////// active + $('.persistent-active').each(function() { + var domid=this.id; + tabs_helper.set_from_saved_active_domid(domid); + }); + // on all the children of the persistent-active element + $('.persistent-active>*').on('shown.bs.tab', function (e) { + var active_domid=e.target; + // at this point we have something like + // http://localhost:8080/portal/slice/ple.inria.f14#tab-resources-list + active_domid=active_domid.hash.replace("#",""); + // find out the domid, which is for the nearest ancestor with class + // persistent-active + var domid=$(e.target).closest(".persistent-active").attr('id'); + tabs_helper.store_active_domid(domid,active_domid); + console.log("CLICKED domid="+domid+" active_domid="+active_domid); + }); + }, + +} // global var tabs_helper + +$(document).ready(tabs_helper.init_all_tabs); + (function($){ $.fn.Tabs = function( method ) { // In Bootstrap 3, we need 'shown.bs.tab' instead of 'shown' diff --git a/plugins/tabs/templates/tabs.html b/plugins/tabs/templates/tabs.html index 298dec92..60031f63 100644 --- a/plugins/tabs/templates/tabs.html +++ b/plugins/tabs/templates/tabs.html @@ -1,4 +1,4 @@ -<ul class="nav nav-tabs" id='tabs-{{ domid }}'> +<ul class="nav nav-tabs{%if persistent_active%} persistent-active{%endif%}" id='tabs-{{ domid }}'> {% for son in sons %} <li{% if son.is_active %} class='active'{% endif %}> <a href="#tab-{{ son.domid }}" data-toggle="tab">{{ son.title }}</a> </li> {% endfor %} diff --git a/portal/sliceview.py b/portal/sliceview.py index 35ab8102..753fed26 100644 --- a/portal/sliceview.py +++ b/portal/sliceview.py @@ -199,6 +199,7 @@ class SliceView (LoginRequiredAutoLogoutView): resources_as_3dmap, resources_as_list_area, ], active_domid = 'resources-map', + persistent_active=True, ) main_stack.insert (resources_area) diff --git a/unfold/composite.py b/unfold/composite.py index 16e28f30..f3d42fc4 100644 --- a/unfold/composite.py +++ b/unfold/composite.py @@ -1,13 +1,14 @@ from unfold.plugin import Plugin -# this is a simple base class for plugins that contain/arrange a set of other plugins -# sons is expected to be a list of the contained plugins, and -# active_domid is the domid for the one son that should be displayed as active -# some subclasses of Composite, like e.g. Tabs, will not behave as expected -# if a valid active_domid is not provided - class Composite (Plugin): + """a simple base class for plugins that contain/arrange a set of other plugins +sons is expected to be a list of the contained plugins, and +active_domid is the domid for the one son that should be displayed as active +some subclasses of Composite, like e.g. Tabs, will not behave as expected +if a valid active_domid is not provided +""" + def __init__ (self, sons=None, active_domid=None, *args, **kwds): Plugin.__init__ (self, *args, **kwds) self.sons= sons if sons else [] diff --git a/unfold/static/js/plugin_helper.js b/unfold/static/js/plugin_helper.js index d15e7a71..7f81a3c3 100644 --- a/unfold/static/js/plugin_helper.js +++ b/unfold/static/js/plugin_helper.js @@ -1,18 +1,19 @@ // // storing toggle's status in localStorage // NOTE that localStorage only stores strings, so true becomes "true" +// var plugin_helper = { debug:false, ////////// use local storage to remember open/closed toggles - store_status : function (domid,status) { + store_toggle_status : function (domid,status) { var key='toggle.'+domid; if (plugin_helper.debug) messages.debug("storing toggle status " + status + " for " + domid + " key=" + key); $.localStorage.setItem(key,status); }, // restore last status - retrieve_last_status : function (domid) { + retrieve_last_toggle_status : function (domid) { var key='toggle.'+domid; // don't do anything if nothing stored var retrieved=$.localStorage.getItem(key); @@ -27,20 +28,22 @@ var plugin_helper = { var hidebtn=$('#hide-'+domid); if (status=="true") { plugindiv.slideDown(); hidebtn.show(); showbtn.hide(); } else { plugindiv.slideUp(); hidebtn.hide(); showbtn.show(); } - plugin_helper.store_status(domid,status); + plugin_helper.store_toggle_status(domid,status); }, - set_from_saved_status : function (domid) { - var previous_status=plugin_helper.retrieve_last_status (domid); + set_from_saved_toggle_status : function (domid) { + var previous_status=plugin_helper.retrieve_last_toggle_status (domid); if (plugin_helper.debug) messages.debug("restoring initial status for domid " + domid + " -> " + previous_status); plugin_helper.set_toggle_status (domid,previous_status); }, + + // triggered upon $(document).ready init_all_plugins : function() { // plugins marked as persistent start with all 3 parts turned off // let us first make sure the right parts are turned on $('.persistent-toggle').each(function() { var domid=this.id.replace('complete-',''); - plugin_helper.set_from_saved_status(domid); + plugin_helper.set_from_saved_toggle_status(domid); }); // program the hide buttons so they do the right thing $('.plugin-hide').each(function() { @@ -57,7 +60,7 @@ var plugin_helper = { // arm tooltips $('.plugin-tooltip').each(function(){ $(this).tooltip({'selector':'','placement':'right'}); }); }, -} // global var plugin +} // global var plugin_helper /* upon document completion, we locate all the hide and show areas, * and configure their behaviour