source: t29-www/shared/js-v6/modules/menu.js @ 480

Last change on this file since 480 was 480, checked in by heribert, 10 years ago

Navigation verbessert, IBM 1130 hinzugefügt und andere Änderungen

File size: 15.9 KB
Line 
1/**
2 * t29v6 Menu
3 *
4 * Features:
5 *  - Collapsable (can be expanded by button)
6 *  - Scrollable (can be maked position:fixed by button)
7 *  - Guide beam (footer clone of navigation in a fancy beam like way)
8 *
9 * Language-specific strings are taken from t29.msg.
10 *
11 * (c) 2012 Sven Koeppel <sven@tech..29.de>
12 * Licensed under GPL
13 **/
14
15if(!t29) window.t29 = {}; // the t29 namespace
16 
17t29.menu = { collapsed:{}, scroll:{}, guide:{} }; // mit Unterklassen
18t29.menu.setup = function() {
19        t29.menu.side = $("nav.side");   // Hauptseitennavigation
20        t29.menu.beam = $("nav.guide");  // Strahlnavigation/Guide (Kopie von side)
21        t29.menu.rel = $("nav.rel");     // relative navigation im footer (vor/zurück)
22        t29.menu.collapsed.setup();
23        t29.menu.scroll.setup();
24       
25        // t29v6 launch: Guide-Menü erst mal deaktiviert
26        //t29.menu.guide.setup();
27};
28
29/***************************************************************************************
30  1. Collapsable Menu system  t29.menu.collapsed
31 
32     The collapsable menu system is capable of collapse parts of the menu by user
33     interaction or program request. The current state is stored in t29.prefs.
34
35***************************************************************************************/
36
37/**
38 * Construct a new collapsible. It needs three named arguments:
39 *   c = new t29.menu.Collapsible({
40 *       id: 'foobar',  // a senseful id for the t29.prefs cookie system
41 *       lists: $(".your ul"),  // some element which should be collapsed
42 *       button: $("<button/>").appendTo("body"); // some button which should do the work
43 *   });
44 *
45 **/
46t29.menu.Collapsible = function(arglist) {
47        for (var n in arguments[0]) { this[n] = arguments[0][n]; }
48        this.store_key = 'menu-collapsible-'+this.id; // key for t29.prefs.get/set
49
50        // default values
51        if(!this.button) // button widget
52                this.button_box = $('<li class="button-box"></li>'); // in nav.side
53                this.button_box.appendTo("nav.side li.collapsible-button-after");
54       
55                this.button = $('<span class="button collapse-menu"></span>')
56                              .addClass('for-'+this.id).//appendTo("nav.side");
57                                                                    appendTo(this.button_box); 
58                                         
59    if(!this.label) { // button label
60                this.label = {};
61                this.label[t29c.FOLD] = t29._("js-menu-collapse-out");
62                this.label[t29c.EXPAND] = t29._("js-menu-collapse-in");
63        }
64        if(!this.initial) // initial state
65                this.initial = t29.prefs.get(this.store_key, t29c.FOLD);
66
67        // set initial state
68        this.set(this.initial, t29c.QUICK);
69       
70        // set button callback
71        this.button.click($.proxy(function(){ this.set(); }, this));
72}
73
74// Constants:
75if(!window.t29c) window.t29c = {}; // namespace for t29 contstants
76t29c.FOLD = true; // state: folded menu (small)
77t29c.EXPAND = false; // state: expanded menu (big)
78t29c.QUICK = true; // action: quick crossover (no animation, instantanous)
79t29c.ANIMATE = false; // action: animated crossover (visible to user)
80
81/**
82 * Menu ein- oder ausklappen.
83 *
84 * @param target_state  true: Eingeklappt, false: ausgeklappt
85 * @param quick  true=keine Animation, false/undefined=Animation
86 **/
87t29.menu.Collapsible.prototype.set = function(collapse, quick) {
88        if(collapse == undefined)
89                collapse = ! this.is_collapsed();
90        log("Collapse: "+this.id+" FOLD " +(collapse==t29c.FOLD ? "<=" : "=>")+" EXPAND " + (quick==t29c.QUICK ? "[quick!]" : ""));
91        if(this.set_pre)
92                this.set_pre(collapse, quick); // execute some callback
93        if(quick) this.lists[collapse ? 'hide' : 'show']();
94        else      this.lists[collapse ? 'slideUp' : 'slideDown']();
95        this.button.text(this.label[collapse]);
96        // body CSS class shall only be used for CSS interaction, not for JS readout. Use is_collapsed() instead.
97        $("body")[collapse ? 'addClass' : 'removeClass']("collapsed-menu-"+this.id);
98        t29.prefs.set(this.store_key, collapse);
99}
100
101// returns whether menu is collapsed (boolean).
102t29.menu.Collapsible.prototype.is_collapsed = function() { return t29.prefs.get(this.store_key) == t29c.FOLD; }
103
104t29.menu.collapsed.setup = function() {
105        // set up some collapsible lists
106        t29.menu.collapsed.u3 = new t29.menu.Collapsible({
107                id: 'u3',
108                lists: $("nav.side .u3").not("nav.side li.active > .u3"), //, .geraete"),
109        });
110       
111        // check if we want mini menu for the beginning
112        if( $("body").hasClass("in-geraete") ) {
113                t29.menu.collapsed.u3.button.hide();
114                // mini doesn't care about cookie settings.
115                t29.menu.collapsed.mini = new t29.menu.Collapsible({
116                        id: 'mini',
117                        lists: $("nav.side li").not('.guide-only').not("li.active, li.active > ul.u3 > li, li.active > ul.u4 > li"),
118                        initial: t29c.FOLD,
119                        set_pre: function(collapse) {
120                                if(collapse == t29c.EXPAND) {
121                                        // after first expanding, disable system and enable rest of systems
122                                        this.button.hide();
123                                        t29.menu.collapsed.u3.button.show();
124                                }
125                        }
126                });
127        }
128       
129        /*
130        t29.menu.collapsed.geraete = new t29.menu.Collapsible({
131                id: 'geraete',
132                lists: $("nav.side ul.geraete"),
133                label: (function(){ l = {}; l[t29c.FOLD] = '(+ extra)';  l[t29c.EXPAND] = '(- extra)'; return l; })(),
134        });
135        */
136
137        // special situation on gerate pages (body.in-geraete): only active li's are shown there
138        // by default. This is a third state next to FOLDed and EXPANDed menu: super-FOLDED.
139        // Clicking the 'details' button yields ordinary FOLDed state.
140       
141        // hide geraete
142        //t29.menu.collapsed.geraete.button.hide();
143        //$("ul.geraete").hide();
144};
145
146/***************************************************************************************
147  2. Menu scroll system  t29.menu.scroll
148 
149     The scrollable menu system can handle a position:fixed navigation area with dynamic
150     switching to static or absolute positioning. It is narrowly toothed to the
151     collapse system. Current state is stored in t29.prefs.
152
153***************************************************************************************/
154
155// enums, die CSS-Klassen im <html> entsprechen:
156t29.menu.scroll.States = Object.freeze({STATIC:"static-menu",FIX:"fixed-menu",STICK_TOP:"stick-top-menu",STICK_BOTTOM:"stick-bottom-menu"});
157/**
158 * Menuezustand beim Scrollen umschalten.
159 * @param target_state Zustand aus scroll.States-Enum
160 * @param
161 *
162 **/
163t29.menu.scroll.set = function(target_state) {
164        old_state = t29.menu.scroll.state;
165        t29.menu.scroll.state = target_state;
166        $("html").removeClass("static-menu fixed-menu stick-top-menu stick-bottom-menu").addClass(t29.menu.scroll.state);
167       
168        // Aufraeumen nach altem Status:
169        switch(old_state) {
170                case t29.menu.scroll.States.STICK_BOTTOM:
171                        t29.menu.side.attr("style",""); // reset css "top" value for positioning
172                        break;
173        }
174       
175        // Einrichten des neuen Status:
176        log("Gehe in Scroll-Zustand "+target_state);
177        switch(target_state) {
178                case t29.menu.scroll.States.STICK_TOP:
179                        // Menue schlaegt obene an. Prinzipiell Gleicher Zustand wie STATIC. Weiter.
180                case t29.menu.scroll.States.STATIC:
181                        // die CSS-Klassen regeln eigentlich alles.
182                        //CSS// t29.menu.collapsed.u3.button.show();
183                        t29.menu.scroll.but.text(t29._("js-menu-scroll-show"));
184                        t29.menu.side.show();
185                        break;
186                case t29.menu.scroll.States.FIX:
187                        // checken ob fixing ueberhaupt geht
188                        /*
189                        if( !t29.menu.collapsed.is() && t29.menu.side.height() > $(window).height()) {
190                                // Navi ist gerade ausgeklappt und zu groß fuer Bildschirm. Probiere Einklappen:
191                                t29.menu.collapsed.set(true, true);
192                                if(t29.menu.side.height() > $(window).height()) {
193                                        // Navi ist auch eingeklappt noch zu groß!
194                                        log("Navi ist auch eingeklappt zu groß zum fixen!");
195                                        // eingeklappt lassen. Weitermachen.
196                                        // hier noch was ueberlegen: Bei zu kleinem Bildschirm
197                                        // sollte der Button gar nicht erst angezeigt werden.
198                                        // dazu braucht man einen resize-Listener, der aber im
199                                        // ausgeklappten zustand jedesmal checken müsste, ob das
200                                        // eingeklappte menue reinpasst. (werte muss man cachen)
201                                        // Ziemlich doofe Aufgabe.
202                                }
203                        }*/
204
205                        t29.menu.collapsed.u3.set(true, true); // Sicherstellen, dass Navi eingeklappt.
206                        //CSS// t29.menu.collapsed.u3.button.hide(); // Ausgeklappte Navi passt auf keinen Bildschirm.
207                        t29.menu.scroll.but.text(t29._("js-menu-scroll-hide"));
208                        break;
209                case t29.menu.scroll.States.STICK_BOTTOM:
210                        // Position absolute; Top-Position manuell setzen.
211                        t29.menu.side.css({top: t29.menu.scroll.stick_bottom });
212                        break;
213                default:
214                        log("Schlechter Zustand: "+target_state);
215        }
216}
217
218t29.menu.scroll.setup = function() {
219        t29.menu.scroll.but = $('<span class="button get-menu"></span>').appendTo("section.sidebar");
220
221        /**
222         * t29.prefs and t29.menu.scroll: Valid values are only FIX and STATIC.
223         * Crossover states like STICK_BOTTOM, STICK_TOP shall not be stored.
224         **/
225        t29.menu.scroll.store_key = 'menu-scroll'; // key for accessing t29.prefs value
226       
227        // set initial state:
228        initial = t29.prefs.get(t29.menu.scroll.store_key, t29.menu.scroll.States.STATIC);
229        switch(initial) {
230                case t29.menu.scroll.States.STATIC:
231                        t29.menu.scroll.set(initial);
232                        break;
233                case t29.menu.scroll.States.FIX:
234                        // davon ausgehend, dass wir uns ganz oben befinden beim Seiten-Laden.
235                        // TODO / PENDING: Wenn man mit Anker #foobar auf die Seite reinkommt,
236                        //                 ist das nicht der Fall! Das kann kombiniert werden
237                        //                 mit t29.smoothscroll!
238                        t29.menu.scroll.set(t29.menu.scroll.States.STICK_TOP);
239                        break;
240                default:
241                        log("t29.menu.scroll.setup: Invalid value "+initial+" for initial prefs");
242        }
243       
244        t29.menu.scroll.but.click(function(){
245                switch(t29.menu.scroll.state) {
246                        case t29.menu.scroll.States.STATIC:
247                                // zu Fix uebergehen, mit Animation.
248                                t29.menu.side.hide();
249                                t29.menu.scroll.set(t29.menu.scroll.States.FIX);
250                                t29.menu.side.fadeIn();
251                                t29.prefs.set(t29.menu.scroll.store_key, t29.menu.scroll.States.FIX);
252                                break;
253                        case t29.menu.scroll.States.FIX:
254                        case t29.menu.scroll.States.STICK_BOTTOM:
255                                // zu Static uebergehen, mit Animation.
256                                t29.menu.side.fadeOut(function(){
257                                        t29.menu.scroll.set(t29.menu.scroll.States.STATIC);
258                                });
259                                t29.prefs.set(t29.menu.scroll.store_key, t29.menu.scroll.States.STATIC);
260                                break;
261                        case t29.menu.scroll.States.STICK_TOP:
262                        default:
263                                // diese Faelle sollten nicht vorkommen.
264                                log("Get-Menu Scroll-Button gedrückt obwohl unmöglich; state="+t29.menu.scroll.state);
265                }
266        }); // end event t29.menu.scroll.but.click.
267       
268        // nun ein paar Y-Koordinaten. berechnet mit dem Ausgangs-menu.side (STATIC).
269        t29.menu.scroll.origin_top = t29.menu.side.offset().top;
270        t29.menu.scroll.max_bottom = $("footer").offset().top - t29.menu.side.height();
271        t29.menu.scroll.stick_bottom = $("footer").offset().top - t29.menu.side.height() - $("#background-color-container").offset().top;
272        t29.menu.scroll.button_max_bottom = $("footer").offset().top;
273        //t29.menu.scroll.max_bottom - $("#background-color-container").offset().top;
274
275        $(window).scroll(function(){
276                var y = $(this).scrollTop();
277
278                switch(t29.menu.scroll.state) {
279                        case t29.menu.scroll.States.STATIC: break; // System inaktiv.
280                        case t29.menu.scroll.States.FIX: 
281                                if(y < t29.menu.scroll.origin_top)
282                                        t29.menu.scroll.set(t29.menu.scroll.States.STICK_TOP);
283                                else if(y > t29.menu.scroll.max_bottom)
284                                        t29.menu.scroll.set(t29.menu.scroll.States.STICK_BOTTOM);
285                                break;
286                        case t29.menu.scroll.States.STICK_TOP:
287                                if(y > t29.menu.scroll.origin_top) {
288                                        // wir sind wieder weiter runter gescrollt.
289                                        if(t29.menu.collapsed.u3.is_collapsed())
290                                                // und der Benutzer hat zwischenzeitlich nicht das Menue ausgeklappt
291                                                t29.menu.scroll.set(t29.menu.scroll.States.FIX);
292                                        else
293                                                // der Benutzer hat zwischenzeitlich ausgeklappt. Schalte fixing aus.
294                                                t29.menu.scroll.set(t29.menu.scroll.States.STATIC);
295                                }
296                                break;
297                        case t29.menu.scroll.States.STICK_BOTTOM:
298                                if(y < t29.menu.scroll.max_bottom) {
299                                        // wir sind wieder weiter hoch gescrollt. Entcollapsen bieten wir ganz
300                                        // unten nicht an. Ergo: Fixing wieder einschalten.
301                                        t29.menu.scroll.set(t29.menu.scroll.States.FIX);
302                                }
303                                break;
304                }
305
306                // Sichtbarkeit des Fixed-Buttons am unteren Seitenrand
307                // festlegen:
308                if(y + $(window).height() > t29.menu.scroll.button_max_bottom) {
309                        $("html").addClass('button-stick-bottom');
310                } else if($("html").hasClass('button-stick-bottom')) {
311                        $("html").removeClass('button-stick-bottom');
312                }
313        }); // end event window.scroll.
314}; // end t29.menu.scroll.setup.
315
316
317/***************************************************************************************
318  2. Footer Guided Tour System   t29.menu.guide
319 
320     The "beam" is a fancy jquery application where the menu is cloned and displayed
321     in the footer in a very other way. This is quite static compared to the
322     applications above.
323
324***************************************************************************************/
325t29.menu.guide.setup = function() {
326        // Beam nur anzeigen wenn auf Seite, die auch in der Beamnavigation drin ist,
327        // sprich Seitenleiste.
328        if(t29.conf['seite_in_nav'] != t29.menu.side.attr('class'))
329                return;
330
331        // Zentraler Hauptschritt: Das Menue ab oberster Ebene klonen und im Footer dranhaengen,
332        // ausserdem ein paar Ummodellierungen.
333        g = t29.menu.beam;
334        t29.menu.side.find(".u1").clone().appendTo(g);
335        g.find("ul").show(); // durch t29.menu.collapse.setup wurden die .u3er auf hide gesetzt. Revert!
336        g.find(".geraete").remove(); // geraete-Links nicht anzeigen
337       
338
339        // Texte ersetzen durch laengere verstaendlichere Beschreibungen im title
340        g.find("a[title]").each(function(){
341                $(this).text( $(this).attr('title') ).attr('title',''); // title attribut entfernen
342        });
343
344        // Abkuerzungen und Wrappings
345        a = g.find("a"); li = g.find("li");
346        a.wrapInner("<span class='text'/>").append("<span class='bullet'/>");
347
348        // Punkte aequidistant verteilen
349        count = a.length;
350        bwidth = $(".bullet",g).outerWidth();
351        each_width = (g.width() + bwidth*2) / count;
352        each_width = g.width() / count;
353       
354       
355        // problem: each_width = 16.384023... -> Math.floor liefert zu schmale Werte, direktes
356        // a.width(each_width) hingegen kann mit Fliesskomma nicht umgehen. Daher jetzt ein Ansatz,
357        // CSS3-Subpixelwerte mit ueberschaubar vielen Dezimalstellen anzuwenden.
358        roundNumber = function(num,dec) { return Math.round(num*Math.pow(10,dec))/Math.pow(10,dec); };
359        subpixel_width = roundNumber(each_width, 2);
360        a.css("width", subpixel_width+"px");
361        //a.css("width", Math.floor(each_width)+"px");
362        // text-Label zentriert darstellen um den Punkt
363        $(".text", a).css("left", function(){return(bwidth - $(this).width())/2; });
364       
365        default_visibles = g.find(".start, .end, .current");
366        default_visibles.addClass("visible"); //.find("a").css("z-index",0);
367        default_visibles = default_visibles.find("a:first-child"); // von li auf a
368       
369        // Overlappings finden
370        // left,right: Funktionen geben links/rechts-Offset des Objekts wieder
371        left = function($o) { return $o.offset().left; }
372        right = function($o) { return $o.offset().left + $o.width(); }
373        edges = default_visibles.map(function(){
374                t = $(".text", this);
375                return {'left': left(t), 'right': right(t) };
376        });
377        min_left = Math.min.apply(null, edges.map(function(){ return this.left }));
378        max_right = Math.max.apply(null, edges.map(function(){ return this.right; }));
379        a.not(default_visibles).each(function(){
380                t = $(".text", this); this_a = $(this);
381                l = left(t); r = right(t);
382                edges.each(function(i){
383                        if((l < this.right && l > this.left) || // rechte kante drin
384                           (r > this.left && r < this.right) || // linke kante drin
385                           (l < this.right && l < min_left)  ||
386                           (r > this.left && r > max_right)) {
387                                        this_a.addClass("higher-text");
388                        }
389                });
390        });
391       
392        // Split position for relative navigation
393        // 20px von nav.side margin left; 40px = 4*10px padding left von nav.rel a
394        ///// 22.04.2012: Deaktiviert, weil anderes Design vor Augen.
395        /*
396        split = $(".current a",g).offset().left - g.offset().left + bwidth/2;
397        rest = $("#content").outerWidth() - split - 40;
398        t29.menu.rel.find(".prev a").width(split);
399        t29.menu.rel.find(".next a").width(rest);
400        */
401};
Note: See TracBrowser for help on using the repository browser.
© 2008 - 2013 technikum29 • Sven Köppel • Some rights reserved
Powered by Trac
Expect where otherwise noted, content on this site is licensed under a Creative Commons 3.0 License