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', ]
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):
+// 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'
-<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 %}
resources_as_3dmap,
resources_as_list_area, ],
active_domid = 'resources-map',
+ persistent_active=True,
)
main_stack.insert (resources_area)
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 []
//
// 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);
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() {
// 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