1 2 /** 3 * @param {Object} opts options 4 * @param {string} [opts.timleine_id] the id of the timeline we're navigating. Default is 'timeline' 5 * @param {string} [opts.selected_class] the class of the currently selected entry. default is 'ui-selected' 6 * @param {string} [opts.entry_class] the class of an entry in the timeline. default is 'entry' 7 * @param {string} [opts.keyup] the shortcut code for the up navigation. default is 'up' (the up arrow) 8 * @param {string} [opts.keydown] the shortcut code for the down navigation. default is 'down' (the down arrow) 9 * @constructor 10 */ 11 var SpazTimelineNav = function(opts) { 12 13 var thisNav = this; 14 15 opts = sch.defaults({ 16 'timeline_id' :'timeline', 17 'selected_class':'ui-selected', 18 'entry_class' :'entry', 19 'keyup':'up', 20 'keydown':'down' 21 }, opts); 22 23 this.selected_class = opts.selected_class; 24 this.timeline_id = opts.timeline_id; 25 this.entry_class = opts.entry_class; 26 this.scroll_offset = opts.scroll_offset; 27 this.keyup = opts.keyup; 28 this.keydown = opts.keydown; 29 30 this.enabled = false; 31 32 this.init(); 33 }; 34 35 36 SpazTimelineNav.prototype.init = function() { 37 var thisNav = this; 38 39 this.enabled = true; 40 41 this.jqtl = jQuery('#'+this.timeline_id); 42 this.jqentries = jQuery('.'+this.entry_class, this.jqtl); 43 44 /* 45 bind keyboard shortcuts 46 */ 47 sch.key_add(this.keyup, function() { thisNav.move('up'); }); 48 sch.key_add(this.keydown, function() { thisNav.move('down'); }); 49 50 /* 51 add numbers for fun 52 */ 53 this.jqentries.each(function(i) { 54 this.innerHTML = i+':'+this.innerHTML; 55 }); 56 }; 57 58 59 60 SpazTimelineNav.prototype.enable = function(state) { 61 state = !!state; // cast to boolean 62 this.enabled = state; 63 }; 64 65 66 /** 67 * 68 */ 69 SpazTimelineNav.prototype.setTimelineId = function(tlid) { 70 if (tlid.indexOf('#') === 0) { 71 tlid = tlid.replace('#', ''); 72 } 73 this.timeline_id = tlid; 74 }; 75 76 77 SpazTimelineNav.prototype.setSelectedClass = function(sel_class) { 78 if (sel_class.indexOf('.') === 0) { 79 sel_class = sel_class.replace('.', ''); 80 } 81 this.selected_class = sel_class; 82 }; 83 84 85 SpazTimelineNav.prototype.setEntryClass = function(entry_class) { 86 if (entry_class.indexOf('.') === 0) { 87 entry_class = entry_class.replace('.', ''); 88 } 89 this.entry_class = entry_class; 90 }; 91 92 /** 93 * 94 */ 95 SpazTimelineNav.prototype.move = function(dir) { 96 97 var thisNav = this; 98 99 if (this.enabled !== true) { 100 sch.debug('SpazTimelineNav object disabled'); 101 return; 102 } 103 104 // sch.debug(dir); 105 106 var i_first = 0; 107 var i_last = this.jqentries.length-1; 108 var i_sel = -1; 109 110 /* 111 find selected 112 */ 113 this.jqentries.each(function(i) { 114 if (jQuery(this).is('.'+thisNav.selected_class)) { 115 i_sel = i; 116 return false; 117 } 118 }); 119 120 // sch.debug("first:\t"+i_first); 121 // sch.debug("last:\t"+i_last); 122 // sch.debug("old selected:\t"+i_sel); 123 124 switch(dir) { 125 126 case 'up': 127 128 if (i_sel < 0 || i_sel > i_last) { 129 i_sel = i_last; 130 // sch.debug('start at bottom!'); 131 } else if (i_sel === i_first) { 132 i_sel = i_sel; // do not wrap! 133 // sch.debug('do not wrap!'); 134 } else { 135 i_sel--; 136 // sch.debug('move down!'); 137 } 138 139 140 break; 141 142 case 'down': 143 144 if (i_sel < 0 || i_sel > i_last) { 145 i_sel = i_first; 146 // sch.debug('start at top!'); 147 } else if (i_sel === i_last) { 148 i_sel = i_sel; // do not wrap! 149 // sch.debug('do not wrap!'); 150 } else { 151 i_sel++; 152 // sch.debug('move up!'); 153 } 154 155 break; 156 157 default: 158 break; 159 160 } 161 162 this.select(this.jqentries.get(i_sel)); 163 164 // sch.debug("new selected:\t"+i_sel); 165 166 }; 167 168 169 /** 170 * selects a given element by applying a class 171 */ 172 SpazTimelineNav.prototype.select = function(element) { 173 /* 174 Deselect all 175 */ 176 this.jqentries.removeClass(this.selected_class); 177 178 /* 179 select passed element 180 */ 181 jQuery(element).addClass(this.selected_class); 182 183 this.reveal(element); 184 }; 185 186 187 /** 188 * if necessary, scrolls the container to reveal the passed element 189 */ 190 SpazTimelineNav.prototype.reveal = function(element) { 191 192 sch.debug('===================================='); 193 194 var jqel = jQuery(element); 195 196 /* 197 first, figure out if element is visible in porthole 198 */ 199 var viewport_bottom = parseInt(this.jqtl.innerHeight(), 10); 200 var selected_bottom = parseInt(jqel.position().top + jqel.height(), 10); 201 var viewport_top = parseInt(this.jqtl.position().top, 10); 202 var selected_top = parseInt(jqel.position().top, 10); 203 var viewport_height = parseInt(this.jqtl.outerHeight(), 10); 204 var selected_height = parseInt(jqel.outerHeight(), 10); 205 var viewport_scrolltop = parseInt(this.jqtl.scrollTop(), 10); 206 var selected_offset = parseInt(jqel.offset().top, 10); 207 208 209 sch.debug('viewport_bottom:'+viewport_bottom); 210 sch.debug('selected_bottom:'+selected_bottom); 211 sch.debug('viewport_top :'+viewport_top); 212 sch.debug('selected_top :'+selected_top); 213 sch.debug('viewport_height :'+viewport_height); 214 sch.debug('selected_height :'+selected_height); 215 sch.debug("viewport_scrolltop:"+viewport_scrolltop); 216 sch.debug("selected_offset.top:"+selected_offset); 217 218 /* 219 below the fold 220 */ 221 var diff; 222 if ( selected_height+selected_top > viewport_bottom ) { 223 sch.debug("el.bottom is > tl.bottom"); 224 diff = (viewport_height - viewport_top - selected_bottom) * -1; 225 this.jqtl.scrollTop(viewport_scrolltop + diff); 226 } 227 228 /* 229 above the fold 230 */ 231 if ( selected_top < 0 ) { 232 sch.debug("el.top is < tl.top"); 233 diff = selected_top * -1; 234 this.jqtl.scrollTop(viewport_scrolltop - diff); 235 } 236 237 sch.debug("New scrolltop:"+this.jqtl.scrollTop()); 238 239 240 }; 241