1 /*jslint 2 browser: true, 3 nomen: false, 4 debug: true, 5 forin: true, 6 plusplus: false, 7 undef: true, 8 white: false, 9 onevar: false 10 */ 11 var sc, jQuery; 12 13 /** 14 * @fileOverview File containing the SpazTimeline object definition 15 * @author <a href="mailto:coj@funkatron.com">coj@funkatron.com</a> 16 */ 17 18 19 /** 20 * This object provides an API for managing the content of a timeline 21 * Currently this requires jQuery, but that could change or be overwritten 22 * on a per-app basis 23 * @requires jQuery 24 * @constructor 25 */ 26 var SpazTimeline = function(opts) { 27 28 var thisTL = this; 29 /** 30 * This is a wrapper function for the refresher interval 31 * we define this here and use a closure to solve a scope issue when the interval fires 32 * @function 33 */ 34 this.refresh = function() { 35 sch.debug('Refreshing timeline'); 36 thisTL.requestData.call(thisTL); 37 }; 38 39 40 /** 41 * Again, due to scope issues, we define this here to take advantage of the closure 42 */ 43 this.onSuccess = function(e, data) { 44 sch.debug('onSuccess timeline'); 45 thisTL.data_success.call(thisTL, e, data); 46 thisTL.startRefresher(); 47 }; 48 49 /** 50 * Again, due to scope issues, we define this here to take advantage of the closure 51 */ 52 this.onFailure = function(e, data) { 53 sch.debug('onFailure timeline'); 54 thisTL.data_failure.call(thisTL, e, data); 55 thisTL.startRefresher(); 56 }; 57 58 59 /* 60 By breaking this out, we can more easily override the 61 constructor process 62 */ 63 this._init(opts); 64 }; 65 66 67 SpazTimeline.prototype._init = function(opts) { 68 69 opts = opts || {}; 70 71 this.max_items = opts.max_items || 100; 72 this.refresh_time = opts.refresh_time || 1000*60*2; // mseconds 73 74 this.timeline_container_selector = opts.timeline_container_selector || '#timeline'; 75 this.timeline_item_selector = opts.timeline_item_selector || 'div.timeline-entry'; 76 // this.entry_relative_time_selector= opts.entry_relative_time_selector|| '.date'; 77 this.event_target = opts.event_target || jQuery(this.timeline_container_selector).get(0); 78 79 this.add_method = opts.add_method || 'prepend'; // prepend or append 80 81 this.success_event = opts.success_event || 'timeline-success'; 82 this.failure_event = opts.failure_event || 'timeline-failure'; 83 84 this.renderer = opts.renderer || null; // required 85 this.request_data = opts.request_data || null; // required 86 this.data_success = opts.data_success || null; // required 87 this.data_failure = opts.data_failure || null; 88 this.refresher = opts.refresher || null; 89 90 if (!this.renderer) { 91 throw new Error ("renderer is required"); 92 } 93 if (!this.request_data) { 94 throw new Error ("request_data is required"); 95 } 96 if (!this.data_success) { 97 throw new Error ("data_success is required"); 98 } 99 100 this.container = jQuery(this.timeline_container_selector).get(0); 101 102 103 }; 104 105 /** 106 * the timeline 107 */ 108 SpazTimeline.prototype.last_id = -1; 109 110 /** 111 * an array of data items that are represented in the timeline 112 */ 113 SpazTimeline.prototype.model = []; 114 115 /** 116 * call this after initialization 117 */ 118 SpazTimeline.prototype.start = function() { 119 sch.debug('Starting timeline'); 120 this.requestData(); 121 }; 122 123 124 /** 125 * This is the method that gets data from the model and calls addItems() on what is returned 126 * 127 * @todo needs to be written to handle async call 128 */ 129 SpazTimeline.prototype.requestData = function() { 130 sch.debug('Requesting data timeline'); 131 this.stopRefresher(); 132 133 this.stopListening(); 134 this.startListening(); 135 136 // call an appropriate model function 137 var items = this.request_data(); 138 }; 139 140 141 142 SpazTimeline.prototype.startListening = function() { 143 var thisTL = this; 144 sc.helpers.debug("Listening for "+thisTL.success_event); 145 sc.helpers.listen(thisTL.event_target, thisTL.success_event, thisTL.onSuccess); 146 sc.helpers.listen(thisTL.event_target, thisTL.failure_event, thisTL.onFailure); 147 }; 148 149 150 SpazTimeline.prototype.stopListening = function() { 151 var thisTL = this; 152 sc.helpers.debug("Stopping listening for "+thisTL.success_event); 153 sc.helpers.unlisten(thisTL.event_target, thisTL.success_event); 154 sc.helpers.unlisten(thisTL.event_target, thisTL.failure_event); 155 }; 156 157 SpazTimeline.prototype.startRefresher = function() { 158 this.stopRefresher(); 159 160 sc.helpers.debug('Starting refresher'); 161 if (this.refresh_time > 1000) { // the minimum refresh is 1000ms. Otherwise we don't auto-refresh 162 sc.helpers.debug('Refresh time is '+this.refresh_time+'ms'); 163 this.refresher = setInterval(this.refresh, this.refresh_time); 164 } else { 165 sc.helpers.debug('Not starting refresher; refresh time is '+this.refresh_time+'ms'); 166 } 167 }; 168 169 170 SpazTimeline.prototype.stopRefresher = function() { 171 sc.helpers.debug('Stopping refresher'); 172 clearInterval(this.refresher); 173 }; 174 175 176 177 178 179 180 /** 181 * Stuff we should do when we're done using this, including 182 * removing event listeners an stopping the refresher 183 */ 184 SpazTimeline.prototype.cleanup = function() { 185 sch.debug('Cleaning up timeline'); 186 this.stopListening(); 187 this.stopRefresher(); 188 }; 189 190 /** 191 * given an array of objects, this will render them and add them to the timeline 192 * @param {array} items 193 */ 194 SpazTimeline.prototype.addItems = function(items) { 195 sch.debug('Adding items to timeline'); 196 197 var items_html = []; 198 var timeline_html = ''; 199 200 for (var x=0; x<items.length; x++) { 201 items_html.push( this.renderItem(items[x], this.renderer) ); 202 } 203 204 if (this.add_method === 'append') { 205 items_html.reverse(); 206 // timeline_html = '<div>'+items_html.join('')+'</div>'; 207 timeline_html = items_html.join(''); 208 this.append(timeline_html); 209 } else { 210 // timeline_html = '<div>'+items_html.join('')+'</div>'; 211 timeline_html = items_html.join(''); 212 this.prepend(timeline_html); 213 } 214 215 this.removeExtraItems(); 216 217 }; 218 219 SpazTimeline.prototype.renderItem = function(item, templatefunc) { 220 sch.debug('Rendering item in timeline'); 221 222 var html = templatefunc(item); 223 224 return html; 225 226 }; 227 228 229 SpazTimeline.prototype.removeExtraItems = function() { 230 231 sch.debug('Removing extra items in timeline'); 232 233 if (this.add_method === 'append') { 234 var remove_from_top = true; 235 } else { 236 remove_from_top = false; 237 } 238 239 sc.helpers.removeExtraElements(this.getEntrySelector(), this.max_items, remove_from_top); 240 }; 241 242 243 SpazTimeline.prototype.removeItems = function(selector) {}; 244 245 246 SpazTimeline.prototype.removeItem = function(selector) {}; 247 248 /** 249 * @param {string} selector 250 * @return {boolean} 251 */ 252 SpazTimeline.prototype.itemExists = function(selector) { 253 254 sch.debug('Checking it item ('+selector+') exists in timeline'); 255 256 var items = this.select(selector); 257 if (items.length>0) { 258 return true; 259 } else { 260 return false; 261 } 262 263 }; 264 265 266 SpazTimeline.prototype.hideItems = function(selector) { 267 sch.debug('Hiding items in timeline'); 268 269 this.filterItems(selector, 'blacklist'); 270 }; 271 272 273 SpazTimeline.prototype.showItems = function(selector) { 274 sch.debug('Showing items in timeline'); 275 276 this.filterItems(selector, 'whitelist'); 277 }; 278 279 280 /** 281 * @param {string} selector 282 * @param {string} type "whitelist" or "blacklist" 283 */ 284 SpazTimeline.prototype.filterItems = function(selector, type) {}; 285 286 287 /** 288 * sorts the elements in the timeline according to the sorting function 289 */ 290 SpazTimeline.prototype.sortItems = function(selector, sortfunc) { 291 292 sch.debug('Sorting items in timeline'); 293 294 var items = this.select(selector); 295 items.sort(sortfunc); 296 }; 297 298 299 300 /** 301 * This is a wrapper for the selector engine, so someone could swap in 302 * their own recipe if necessary. By default we use jQuery, and return the 303 * array of HTML elements (not the jQuery object) 304 * @type DOMelement[] 305 */ 306 SpazTimeline.prototype.select = function(selector, container) { 307 if (!container) { 308 container = this.timeline_container_selector; 309 } 310 return jQuery(selector, container).get(); 311 }; 312 313 /** 314 * wrapper for prepending to timeline 315 */ 316 SpazTimeline.prototype.prepend = function(htmlitem) { 317 jQuery(this.timeline_container_selector).prepend(htmlitem); 318 }; 319 SpazTimeline.prototype.append = function(htmlitem) { 320 jQuery(this.timeline_container_selector).append(htmlitem); 321 }; 322 323 SpazTimeline.prototype.getEntrySelector = function() { 324 return this.timeline_container_selector + ' ' + this.timeline_item_selector; 325 }; 326