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

Last change on this file since 279 was 279, checked in by sven, 12 years ago

Design: Linkfarben, Footerrelationslinks, Einblendlogik von Relationen.

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