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