1/* 2 jQuery News Ticker is free software: you can redistribute it and/or modify 3 it under the terms of the GNU General Public License as published by 4 the Free Software Foundation, version 2 of the License. 5 6 jQuery News Ticker is distributed in the hope that it will be useful, 7 but WITHOUT ANY WARRANTY; without even the implied warranty of 8 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 GNU General Public License for more details. 10 11 You should have received a copy of the GNU General Public License 12 along with jQuery News Ticker. If not, see <http://www.gnu.org/licenses/>. 13*/ 14(function($){ 15 $.fn.ticker = function(options) { 16 // Extend our default options with those provided. 17 // Note that the first arg to extend is an empty object - 18 // this is to keep from overriding our "defaults" object. 19 var opts = $.extend({}, $.fn.ticker.defaults, options); 20 21 // check that the passed element is actually in the DOM 22 if ($(this).length == 0) { 23 if (window.console && window.console.log) { 24 window.console.log('Element does not exist in DOM!'); 25 } 26 else { 27 alert('Element does not exist in DOM!'); 28 } 29 return false; 30 } 31 32 /* Get the id of the UL to get our news content from */ 33 var newsID = '#' + $(this).attr('id'); 34 35 /* Get the tag type - we will check this later to makde sure it is a UL tag */ 36 var tagType = $(this).get(0).tagName; 37 38 return this.each(function() { 39 // get a unique id for this ticker 40 var uniqID = getUniqID(); 41 42 /* Internal vars */ 43 var settings = { 44 position: 0, 45 time: 0, 46 distance: 0, 47 newsArr: {}, 48 play: true, 49 paused: false, 50 contentLoaded: false, 51 dom: { 52 contentID: '#ticker-content-' + uniqID, 53 titleID: '#ticker-title-' + uniqID, 54 titleElem: '#ticker-title-' + uniqID + ' SPAN', 55 tickerID : '#ticker-' + uniqID, 56 wrapperID: '#ticker-wrapper-' + uniqID, 57 revealID: '#ticker-swipe-' + uniqID, 58 revealElem: '#ticker-swipe-' + uniqID + ' SPAN', 59 controlsID: '#ticker-controls-' + uniqID, 60 prevID: '#prev-' + uniqID, 61 nextID: '#next-' + uniqID, 62 playPauseID: '#play-pause-' + uniqID 63 } 64 }; 65 66 // if we are not using a UL, display an error message and stop any further execution 67 if (tagType != 'UL' && tagType != 'OL' && opts.htmlFeed === true) { 68 debugError('Cannot use <' + tagType.toLowerCase() + '> type of element for this plugin - must of type <ul> or <ol>'); 69 return false; 70 } 71 72 // set the ticker direction 73 opts.direction == 'rtl' ? opts.direction = 'right' : opts.direction = 'left'; 74 75 // lets go... 76 initialisePage(); 77 /* Function to get the size of an Object*/ 78 function countSize(obj) { 79 var size = 0, key; 80 for (key in obj) { 81 if (obj.hasOwnProperty(key)) size++; 82 } 83 return size; 84 }; 85 86 function getUniqID() { 87 var newDate = new Date; 88 return newDate.getTime(); 89 } 90 91 /* Function for handling debug and error messages */ 92 function debugError(obj) { 93 if (opts.debugMode) { 94 if (window.console && window.console.log) { 95 window.console.log(obj); 96 } 97 else { 98 alert(obj); 99 } 100 } 101 } 102 103 /* Function to setup the page */ 104 function initialisePage() { 105 // process the content for this ticker 106 processContent(); 107 108 // add our HTML structure for the ticker to the DOM 109 $(newsID).wrap('<div id="' + settings.dom.wrapperID.replace('#', '') + '"></div>'); 110 111 // remove any current content inside this ticker 112 $(settings.dom.wrapperID).children().remove(); 113 114 $(settings.dom.wrapperID).append('<div id="' + settings.dom.tickerID.replace('#', '') + '" class="ticker"><div id="' + settings.dom.titleID.replace('#', '') + '" class="ticker-title"><span><!-- --></span></div><p id="' + settings.dom.contentID.replace('#', '') + '" class="ticker-content"></p><div id="' + settings.dom.revealID.replace('#', '') + '" class="ticker-swipe"><span><!-- --></span></div></div>'); 115 $(settings.dom.wrapperID).removeClass('no-js').addClass('ticker-wrapper has-js ' + opts.direction); 116 // hide the ticker 117 $(settings.dom.tickerElem + ',' + settings.dom.contentID).hide(); 118 // add the controls to the DOM if required 119 if (opts.controls) { 120 // add related events - set functions to run on given event 121 $(settings.dom.controlsID).on('click mouseover mousedown mouseout mouseup', function (e) { 122 var button = e.target.id; 123 if (e.type == 'click') { 124 switch (button) { 125 case settings.dom.prevID.replace('#', ''): 126 // show previous item 127 settings.paused = true; 128 $(settings.dom.playPauseID).addClass('paused'); 129 manualChangeContent('prev'); 130 break; 131 case settings.dom.nextID.replace('#', ''): 132 // show next item 133 settings.paused = true; 134 $(settings.dom.playPauseID).addClass('paused'); 135 manualChangeContent('next'); 136 break; 137 case settings.dom.playPauseID.replace('#', ''): 138 // play or pause the ticker 139 if (settings.play == true) { 140 settings.paused = true; 141 $(settings.dom.playPauseID).addClass('paused'); 142 pauseTicker(); 143 } 144 else { 145 settings.paused = false; 146 $(settings.dom.playPauseID).removeClass('paused'); 147 restartTicker(); 148 } 149 break; 150 } 151 } 152 else if (e.type == 'mouseover' && $('#' + button).hasClass('controls')) { 153 $('#' + button).addClass('over'); 154 } 155 else if (e.type == 'mousedown' && $('#' + button).hasClass('controls')) { 156 $('#' + button).addClass('down'); 157 } 158 else if (e.type == 'mouseup' && $('#' + button).hasClass('controls')) { 159 $('#' + button).removeClass('down'); 160 } 161 else if (e.type == 'mouseout' && $('#' + button).hasClass('controls')) { 162 $('#' + button).removeClass('over'); 163 } 164 });
165 // add controls HTML to DOM 166 $(settings.dom.wrapperID).append('<ul id="' + settings.dom.controlsID.replace('#', '') + '" class="ticker-controls"><li id="' + settings.dom.playPauseID.replace('#', '') + '" class="jnt-play-pause controls"><a href=""><!-- --></a></li><li id="' + settings.dom.prevID.replace('#', '') + '" class="jnt-prev controls"><a href=""><!-- --></a></li><li id="' + settings.dom.nextID.replace('#', '') + '" class="jnt-next controls"><a href=""><!-- --></a></li></ul>'); 167 } 168 if (opts.displayType != 'fade') { 169 // add mouse over on the content 170 $(settings.dom.contentID).mouseover(function () { 171 if (settings.paused == false) { 172 pauseTicker(); 173 } 174 }).mouseout(function () { 175 if (settings.paused == false) { 176 restartTicker(); 177 } 178 }); 179 } 180 // we may have to wait for the ajax call to finish here 181 if (!opts.ajaxFeed) { 182 setupContentAndTriggerDisplay(); 183 } 184 } 185 186 /* Start to process the content for this ticker */ 187 function processContent() { 188 // check to see if we need to load content 189 if (settings.contentLoaded == false) { 190 // construct content 191 if (opts.ajaxFeed) { 192 if (opts.feedType == 'xml') { 193 $.ajax({
194 url: opts.feedUrl, 195 cache: false, 196 dataType: opts.feedType, 197 async: true, 198 success: function(data){ 199 count = 0; 200 // get the 'root' node 201 for (var a = 0; a < data.childNodes.length; a++) { 202 if (data.childNodes[a].nodeName == 'rss') { 203 xmlContent = data.childNodes[a]; 204 } 205 } 206 // find the channel node 207 for (var i = 0; i < xmlContent.childNodes.length; i++) { 208 if (xmlContent.childNodes[i].nodeName == 'channel') { 209 xmlChannel = xmlContent.childNodes[i]; 210 } 211 } 212 // for each item create a link and add the article title as the link text 213 for (var x = 0; x < xmlChannel.childNodes.length; x++) { 214 if (xmlChannel.childNodes[x].nodeName == 'item') { 215 xmlItems = xmlChannel.childNodes[x]; 216 var title, link = false; 217 for (var y = 0; y < xmlItems.childNodes.length; y++) { 218 if (xmlItems.childNodes[y].nodeName == 'title') { 219 title = xmlItems.childNodes[y].lastChild.nodeValue; 220 } 221 else if (xmlItems.childNodes[y].nodeName == 'link') { 222 link = xmlItems.childNodes[y].lastChild.nodeValue; 223 } 224 if ((title !== false && title != '') && link !== false) { 225 settings.newsArr['item-' + count] = { type: opts.titleText, content: '<a href="' + link + '">' + title + '</a>' }; count++; title = false; link = false; 226 } 227 } 228 } 229 } 230 // quick check here to see if we actually have any content - log error if not
231 if (countSize(settings.newsArr < 1)) { 232 debugError('Couldn\'t find any content from the XML feed for the ticker to use!'); 233 return false; 234 } 235 settings.contentLoaded = true; 236 setupContentAndTriggerDisplay(); 237 } 238 }); 239 } 240 else { 241 debugError('Code Me!'); 242 } 243 } 244 else if (opts.htmlFeed) { 245 if($(newsID + ' LI').length > 0) { 246 $(newsID + ' LI').each(function (i) { 247 // maybe this could be one whole object and not an array of objects? 248 settings.newsArr['item-' + i] = { type: opts.titleText, content: $(this).html()}; 249 }); 250 } 251 else { 252 debugError('Couldn\'t find HTML any content for the ticker to use!'); 253 return false; 254 } 255 } 256 else { 257 debugError('The ticker is set to not use any types of content! Check the settings for the ticker.'); 258 return false; 259 } 260 } 261 } 262 263 function setupContentAndTriggerDisplay() { 264 265 settings.contentLoaded = true; 266 267 // update the ticker content with the correct item 268 // insert news content into DOM 269 $(settings.dom.titleElem).html(settings.newsArr['item-' + settings.position].type); 270 $(settings.dom.contentID).html(settings.newsArr['item-' + settings.position].content); 271 272 // set the next content item to be used - loop round if we are at the end of the content
273 if (settings.position == (countSize(settings.newsArr) -1)) { 274 settings.position = 0; 275 } 276 else { 277 settings.position++; 278 } 279 280 // get the values of content and set the time of the reveal (so all reveals have the same speed regardless of content size) 281 distance = $(settings.dom.contentID).width(); 282 time = distance / opts.speed; 283 284 // start the ticker animation 285 revealContent(); 286 } 287 288 // slide back cover or fade in content 289 function revealContent() { 290 $(settings.dom.contentID).css('opacity', '1'); 291 if(settings.play) { 292 // get the width of the title element to offset the content and reveal 293 var offset = $(settings.dom.titleID).width() + 20; 294 295 $(settings.dom.revealID).css(opts.direction, offset + 'px'); 296 // show the reveal element and start the animation 297 if (opts.displayType == 'fade') { 298 // fade in effect ticker 299 $(settings.dom.revealID).hide(0, function () { 300 $(settings.dom.contentID).css(opts.direction, offset + 'px').fadeIn(opts.fadeInSpeed, postReveal); 301 }); 302 } 303 else if (opts.displayType == 'scroll') { 304 // to code 305 } 306 else { 307 // default bbc scroll effect 308 $(settings.dom.revealElem).show(0, function () { 309 $(settings.dom.contentID).css(opts.direction, offset + 'px').show(); 310 // set our animation direction 311 animationAction = opts.direction == 'right' ? { marginRight: distance + 'px'} : { marginLeft: distance + 'px' }; 312 $(settings.dom.revealID).css('margin-' + opts.direction, '0px').delay(20).animate(animationAction, time, 'linear', postReveal); 313 }); 314 } 315 } 316 else { 317 return false; 318 } 319 }; 320 321 // here we hide the current content and reset the ticker elements to a default state ready for the next ticker item 322 function postReveal() { 323 if(settings.play) { 324 // we have to separately fade the content out here to get around an IE bug - needs further investigation 325 $(settings.dom.contentID).delay(opts.pauseOnItems).fadeOut(opts.fadeOutSpeed); 326 // deal with the rest of the content, prepare the DOM and trigger the next ticker 327 if (opts.displayType == 'fade') { 328 $(settings.dom.contentID).fadeOut(opts.fadeOutSpeed, function () { 329 $(settings.dom.wrapperID) 330 .find(settings.dom.revealElem + ',' + settings.dom.contentID) 331 .hide() 332 .end().find(settings.dom.tickerID + ',' + settings.dom.revealID) 333 .show() 334 .end().find(settings.dom.tickerID + ',' + settings.dom.revealID) 335 .removeAttr('style'); 336 setupContentAndTriggerDisplay(); 337 }); 338 } 339 else { 340 $(settings.dom.revealID).hide(0, function () { 341 $(settings.dom.contentID).fadeOut(opts.fadeOutSpeed, function () { 342 $(settings.dom.wrapperID) 343 .find(settings.dom.revealElem + ',' + settings.dom.contentID) 344 .hide() 345 .end().find(settings.dom.tickerID + ',' + settings.dom.revealID) 346 .show() 347 .end().find(settings.dom.tickerID + ',' + settings.dom.revealID) 348 .removeAttr('style'); 349 setupContentAndTriggerDisplay(); 350 }); 351 }); 352 } 353 } 354 else { 355 $(settings.dom.revealElem).hide(); 356 } 357 } 358 359 // pause ticker 360 function pauseTicker() { 361 settings.play = false; 362 // stop animation and show content - must pass "true, true" to the stop function, or we can get some funky behaviour 363 $(settings.dom.tickerID + ',' + settings.dom.revealID + ',' + settings.dom.titleID + ',' + settings.dom.titleElem + ',' + settings.dom.revealElem + ',' + settings.dom.contentID).stop(true, true); 364 $(settings.dom.revealID + ',' + settings.dom.revealElem).hide(); 365 $(settings.dom.wrapperID) 366 .find(settings.dom.titleID + ',' + settings.dom.titleElem).show() 367 .end().find(settings.dom.contentID).show(); 368 } 369 370 // play ticker 371 function restartTicker() { 372 settings.play = true; 373 settings.paused = false; 374 // start the ticker again 375 postReveal(); 376 } 377 378 // change the content on user input 379 function manualChangeContent(direction) { 380 pauseTicker(); 381 switch (direction) { 382 case 'prev': 383 if (settings.position == 0) {
384 settings.position = countSize(settings.newsArr) -2; 385 } 386 else if (settings.position == 1) { 387 settings.position = countSize(settings.newsArr) -1; 388 } 389 else { 390 settings.position = settings.position - 2; 391 } 392 $(settings.dom.titleElem).html(settings.newsArr['item-' + settings.position].type); 393 $(settings.dom.contentID).html(settings.newsArr['item-' + settings.position].content); 394 break; 395 case 'next': 396 $(settings.dom.titleElem).html(settings.newsArr['item-' + settings.position].type); 397 $(settings.dom.contentID).html(settings.newsArr['item-' + settings.position].content); 398 break; 399 } 400 // set the next content item to be used - loop round if we are at the end of the content 401 if (settings.position == (countSize(settings.newsArr) -1)) { 402 settings.position = 0; 403 } 404 else { 405 settings.position++; 406 } 407 } 408 }); 409 }; 410 411 // plugin defaults - added as a property on our plugin function 412 $.fn.ticker.defaults = { 413 speed: 0.10, 414 ajaxFeed: false, 415 feedUrl: '', 416 feedType: 'xml', 417 displayType: 'reveal', 418 htmlFeed: true, 419 debugMode: true, 420 controls: true, 421 titleText: '', 422 direction: 'ltr', 423 pauseOnItems: 3000, 424 fadeInSpeed: 600, 425 fadeOutSpeed: 300 426 }; 427})(jQuery);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.