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