File indexing completed on 2024-05-19 06:00:28
0001 (function ($) { 0002 'use strict'; 0003 0004 var RSS = function (target, url, options, callback) { 0005 this.target = target; 0006 0007 this.url = url; 0008 this.html = []; 0009 this.effectQueue = []; 0010 0011 this.options = $.extend({ 0012 ssl: false, 0013 host: 'www.feedrapp.info', 0014 limit: null, 0015 key: null, 0016 layoutTemplate: '<ul>{entries}</ul>', 0017 entryTemplate: '<li><a href="{url}">[{author}@{date}] {title}</a><br/>{shortBodyPlain}</li>', 0018 tokens: {}, 0019 outputMode: 'json', 0020 dateFormat: 'dddd MMM Do', 0021 dateLocale: 'en', 0022 effect: 'show', 0023 offsetStart: false, 0024 offsetEnd: false, 0025 error: function () { 0026 console.log('jQuery RSS: url doesn\'t link to RSS-Feed'); 0027 }, 0028 onData: function () {}, 0029 success: function () {} 0030 }, options || {}); 0031 0032 this.callback = callback || this.options.success; 0033 }; 0034 0035 RSS.htmlTags = [ 0036 'doctype', 'html', 'head', 'title', 'base', 'link', 'meta', 'style', 'script', 'noscript', 0037 'body', 'article', 'nav', 'aside', 'section', 'header', 'footer', 'h1-h6', 'hgroup', 'address', 0038 'p', 'hr', 'pre', 'blockquote', 'ol', 'ul', 'li', 'dl', 'dt', 'dd', 'figure', 'figcaption', 0039 'div', 'table', 'caption', 'thead', 'tbody', 'tfoot', 'tr', 'th', 'td', 'col', 'colgroup', 0040 'form', 'fieldset', 'legend', 'label', 'input', 'button', 'select', 'datalist', 'optgroup', 0041 'option', 'textarea', 'keygen', 'output', 'progress', 'meter', 'details', 'summary', 'command', 0042 'menu', 'del', 'ins', 'img', 'iframe', 'embed', 'object', 'param', 'video', 'audio', 'source', 0043 'canvas', 'track', 'map', 'area', 'a', 'em', 'strong', 'i', 'b', 'u', 's', 'small', 'abbr', 'q', 0044 'cite', 'dfn', 'sub', 'sup', 'time', 'code', 'kbd', 'samp', 'var', 'mark', 'bdi', 'bdo', 'ruby', 0045 'rt', 'rp', 'span', 'br', 'wbr' 0046 ]; 0047 0048 RSS.prototype.load = function (callback) { 0049 var apiProtocol = 'http' + (this.options.ssl ? 's' : ''); 0050 var apiHost = apiProtocol + '://' + this.options.host; 0051 var apiUrl = apiHost + '?callback=?&q=' + encodeURIComponent(this.url); 0052 0053 // set limit to offsetEnd if offset has been set 0054 if (this.options.offsetStart && this.options.offsetEnd) { 0055 this.options.limit = this.options.offsetEnd; 0056 } 0057 0058 if (this.options.limit !== null) { 0059 apiUrl += '&num=' + this.options.limit; 0060 } 0061 0062 if (this.options.key !== null) { 0063 apiUrl += '&key=' + this.options.key; 0064 } 0065 0066 $.getJSON(apiUrl, callback); 0067 }; 0068 0069 RSS.prototype.render = function () { 0070 var self = this; 0071 0072 this.load(function (data) { 0073 try { 0074 self.feed = data.responseData.feed; 0075 self.entries = data.responseData.feed.entries; 0076 } catch (e) { 0077 self.entries = []; 0078 self.feed = null; 0079 return self.options.error.call(self); 0080 } 0081 0082 var html = self.generateHTMLForEntries(); 0083 0084 self.target.append(html.layout); 0085 0086 if (html.entries.length !== 0) { 0087 if ($.isFunction(self.options.onData)) { 0088 self.options.onData.call(self); 0089 } 0090 0091 var container = $(html.layout).is('entries') ? html.layout : $('entries', html.layout); 0092 0093 self.appendEntriesAndApplyEffects(container, html.entries); 0094 } 0095 0096 if (self.effectQueue.length > 0) { 0097 self.executeEffectQueue(self.callback); 0098 } else if ($.isFunction(self.callback)) { 0099 self.callback.call(self); 0100 } 0101 }); 0102 }; 0103 0104 RSS.prototype.appendEntriesAndApplyEffects = function (target, entries) { 0105 var self = this; 0106 0107 $.each(entries, function (idx, entry) { 0108 var $html = self.wrapContent(entry); 0109 0110 if (self.options.effect === 'show') { 0111 target.before($html); 0112 } else { 0113 $html.css({ display: 'none' }); 0114 target.before($html); 0115 self.applyEffect($html, self.options.effect); 0116 } 0117 }); 0118 0119 target.remove(); 0120 }; 0121 0122 RSS.prototype.generateHTMLForEntries = function () { 0123 var self = this; 0124 var result = { entries: [], layout: null }; 0125 0126 $(this.entries).each(function () { 0127 var entry = this; 0128 var offsetStart = self.options.offsetStart; 0129 var offsetEnd = self.options.offsetEnd; 0130 var evaluatedString; 0131 0132 // offset required 0133 if (offsetStart && offsetEnd) { 0134 if (index >= offsetStart && index <= offsetEnd) { 0135 if (self.isRelevant(entry, result.entries)) { 0136 evaluatedString = self.evaluateStringForEntry( 0137 self.options.entryTemplate, entry 0138 ); 0139 0140 result.entries.push(evaluatedString); 0141 } 0142 } 0143 } else { 0144 // no offset 0145 if (self.isRelevant(entry, result.entries)) { 0146 evaluatedString = self.evaluateStringForEntry( 0147 self.options.entryTemplate, entry 0148 ); 0149 0150 result.entries.push(evaluatedString); 0151 } 0152 } 0153 }); 0154 0155 if (!!this.options.entryTemplate) { 0156 // we have an entryTemplate 0157 result.layout = this.wrapContent( 0158 this.options.layoutTemplate.replace('{entries}', '<entries></entries>') 0159 ); 0160 } else { 0161 // no entryTemplate available 0162 result.layout = this.wrapContent('<div><entries></entries></div>'); 0163 } 0164 0165 return result; 0166 }; 0167 0168 RSS.prototype.wrapContent = function (content) { 0169 if (($.trim(content).indexOf('<') !== 0)) { 0170 // the content has no html => create a surrounding div 0171 return $('<div>' + content + '</div>'); 0172 } else { 0173 // the content has html => don't touch it 0174 return $(content); 0175 } 0176 }; 0177 0178 RSS.prototype.applyEffect = function ($element, effect, callback) { 0179 var self = this; 0180 0181 switch (effect) { 0182 case 'slide': 0183 $element.slideDown('slow', callback); 0184 break; 0185 case 'slideFast': 0186 $element.slideDown(callback); 0187 break; 0188 case 'slideSynced': 0189 self.effectQueue.push({ element: $element, effect: 'slide' }); 0190 break; 0191 case 'slideFastSynced': 0192 self.effectQueue.push({ element: $element, effect: 'slideFast' }); 0193 break; 0194 } 0195 }; 0196 0197 RSS.prototype.executeEffectQueue = function (callback) { 0198 var self = this; 0199 0200 this.effectQueue.reverse(); 0201 0202 var executeEffectQueueItem = function () { 0203 var item = self.effectQueue.pop(); 0204 0205 if (item) { 0206 self.applyEffect(item.element, item.effect, executeEffectQueueItem); 0207 } else if (callback) { 0208 callback(); 0209 } 0210 }; 0211 0212 executeEffectQueueItem(); 0213 }; 0214 0215 RSS.prototype.evaluateStringForEntry = function (string, entry) { 0216 var result = string; 0217 var self = this; 0218 0219 $(string.match(/(\{.*?\})/g)).each(function () { 0220 var token = this.toString(); 0221 0222 result = result.replace(token, self.getValueForToken(token, entry)); 0223 }); 0224 0225 return result; 0226 }; 0227 0228 RSS.prototype.isRelevant = function (entry, entries) { 0229 var tokenMap = this.getTokenMap(entry); 0230 0231 if (this.options.filter) { 0232 if (this.options.filterLimit && (this.options.filterLimit === entries.length)) { 0233 return false; 0234 } else { 0235 return this.options.filter(entry, tokenMap); 0236 } 0237 } else { 0238 return true; 0239 } 0240 }; 0241 0242 RSS.prototype.getFormattedDate = function (dateString) { 0243 // If a custom formatting function is provided, use that. 0244 if (this.options.dateFormatFunction) { 0245 return this.options.dateFormatFunction(dateString); 0246 } else if (typeof moment !== 'undefined') { 0247 // If moment.js is available and dateFormatFunction is not overriding it, 0248 // use it to format the date. 0249 var date = moment(new Date(dateString)); 0250 0251 if (date.locale) { 0252 date = date.locale(this.options.dateLocale); 0253 } else { 0254 date = date.lang(this.options.dateLocale); 0255 } 0256 0257 return date.format(this.options.dateFormat); 0258 } else { 0259 // If all else fails, just use the date as-is. 0260 return dateString; 0261 } 0262 }; 0263 0264 RSS.prototype.getTokenMap = function (entry) { 0265 if (!this.feedTokens) { 0266 var feed = JSON.parse(JSON.stringify(this.feed)); 0267 0268 delete feed.entries; 0269 this.feedTokens = feed; 0270 } 0271 0272 return $.extend({ 0273 feed: this.feedTokens, 0274 url: entry.link, 0275 author: entry.author, 0276 date: this.getFormattedDate(entry.publishedDate), 0277 title: entry.title, 0278 body: entry.content, 0279 shortBody: entry.contentSnippet, 0280 0281 bodyPlain: (function (entry) { 0282 var result = entry.content 0283 .replace(/<script[\\r\\\s\S]*<\/script>/mgi, '') 0284 .replace(/<\/?[^>]+>/gi, ''); 0285 0286 for (var i = 0; i < RSS.htmlTags.length; i++) { 0287 result = result.replace(new RegExp('<' + RSS.htmlTags[i], 'gi'), ''); 0288 } 0289 0290 return result; 0291 })(entry), 0292 0293 shortBodyPlain: entry.contentSnippet.replace(/<\/?[^>]+>/gi, ''), 0294 index: $.inArray(entry, this.entries), 0295 totalEntries: this.entries.length, 0296 0297 teaserImage: (function (entry) { 0298 try { 0299 return entry.content.match(/(<img.*?>)/gi)[0]; 0300 } 0301 catch (e) { 0302 return ''; 0303 } 0304 })(entry), 0305 0306 teaserImageUrl: (function (entry) { 0307 try { 0308 return entry.content.match(/(<img.*?>)/gi)[0].match(/src="(.*?)"/)[1]; 0309 } 0310 catch (e) { 0311 return ''; 0312 } 0313 })(entry) 0314 }, this.options.tokens); 0315 }; 0316 0317 RSS.prototype.getValueForToken = function (_token, entry) { 0318 var tokenMap = this.getTokenMap(entry); 0319 var token = _token.replace(/[\{\}]/g, ''); 0320 var result = tokenMap[token]; 0321 0322 if (typeof result !== 'undefined') { 0323 return ((typeof result === 'function') ? result(entry, tokenMap) : result); 0324 } else { 0325 throw new Error('Unknown token: ' + _token + ', url:' + this.url); 0326 } 0327 }; 0328 0329 $.fn.rss = function (url, options, callback) { 0330 new RSS(this, url, options, callback).render(); 0331 return this; // Implement chaining 0332 }; 0333 })(jQuery);