| (function ($) { |
| 'use strict'; |
| |
| var RSS = function (target, url, options, callback) { |
| this.target = target; |
| |
| this.url = url; |
| this.html = []; |
| this.effectQueue = []; |
| |
| this.options = $.extend({ |
| ssl: false, |
| host: 'www.feedrapp.info', |
| limit: null, |
| key: null, |
| layoutTemplate: '<ul>{entries}</ul>', |
| entryTemplate: '<li><a href="{url}">[{author}@{date}] {title}</a><br/>{shortBodyPlain}</li>', |
| tokens: {}, |
| outputMode: 'json', |
| dateFormat: 'dddd MMM Do', |
| dateLocale: 'en', |
| effect: 'show', |
| offsetStart: false, |
| offsetEnd: false, |
| error: function () { |
| console.log('jQuery RSS: url doesn\'t link to RSS-Feed'); |
| }, |
| onData: function () {}, |
| success: function () {} |
| }, options || {}); |
| |
| this.callback = callback || this.options.success; |
| }; |
| |
| RSS.htmlTags = [ |
| 'doctype', 'html', 'head', 'title', 'base', 'link', 'meta', 'style', 'script', 'noscript', |
| 'body', 'article', 'nav', 'aside', 'section', 'header', 'footer', 'h1-h6', 'hgroup', 'address', |
| 'p', 'hr', 'pre', 'blockquote', 'ol', 'ul', 'li', 'dl', 'dt', 'dd', 'figure', 'figcaption', |
| 'div', 'table', 'caption', 'thead', 'tbody', 'tfoot', 'tr', 'th', 'td', 'col', 'colgroup', |
| 'form', 'fieldset', 'legend', 'label', 'input', 'button', 'select', 'datalist', 'optgroup', |
| 'option', 'textarea', 'keygen', 'output', 'progress', 'meter', 'details', 'summary', 'command', |
| 'menu', 'del', 'ins', 'img', 'iframe', 'embed', 'object', 'param', 'video', 'audio', 'source', |
| 'canvas', 'track', 'map', 'area', 'a', 'em', 'strong', 'i', 'b', 'u', 's', 'small', 'abbr', 'q', |
| 'cite', 'dfn', 'sub', 'sup', 'time', 'code', 'kbd', 'samp', 'var', 'mark', 'bdi', 'bdo', 'ruby', |
| 'rt', 'rp', 'span', 'br', 'wbr' |
| ]; |
| |
| RSS.prototype.load = function (callback) { |
| var apiProtocol = 'http' + (this.options.ssl ? 's' : ''); |
| var apiHost = apiProtocol + '://' + this.options.host; |
| var apiUrl = apiHost + '?callback=?&q=' + encodeURIComponent(this.url); |
| |
| |
| if (this.options.offsetStart && this.options.offsetEnd) { |
| this.options.limit = this.options.offsetEnd; |
| } |
| |
| if (this.options.limit !== null) { |
| apiUrl += '&num=' + this.options.limit; |
| } |
| |
| if (this.options.key !== null) { |
| apiUrl += '&key=' + this.options.key; |
| } |
| |
| $.getJSON(apiUrl, callback); |
| }; |
| |
| RSS.prototype.render = function () { |
| var self = this; |
| |
| this.load(function (data) { |
| try { |
| self.feed = data.responseData.feed; |
| self.entries = data.responseData.feed.entries; |
| } catch (e) { |
| self.entries = []; |
| self.feed = null; |
| return self.options.error.call(self); |
| } |
| |
| var html = self.generateHTMLForEntries(); |
| |
| self.target.append(html.layout); |
| |
| if (html.entries.length !== 0) { |
| if ($.isFunction(self.options.onData)) { |
| self.options.onData.call(self); |
| } |
| |
| var container = $(html.layout).is('entries') ? html.layout : $('entries', html.layout); |
| |
| self.appendEntriesAndApplyEffects(container, html.entries); |
| } |
| |
| if (self.effectQueue.length > 0) { |
| self.executeEffectQueue(self.callback); |
| } else if ($.isFunction(self.callback)) { |
| self.callback.call(self); |
| } |
| }); |
| }; |
| |
| RSS.prototype.appendEntriesAndApplyEffects = function (target, entries) { |
| var self = this; |
| |
| $.each(entries, function (idx, entry) { |
| var $html = self.wrapContent(entry); |
| |
| if (self.options.effect === 'show') { |
| target.before($html); |
| } else { |
| $html.css({ display: 'none' }); |
| target.before($html); |
| self.applyEffect($html, self.options.effect); |
| } |
| }); |
| |
| target.remove(); |
| }; |
| |
| RSS.prototype.generateHTMLForEntries = function () { |
| var self = this; |
| var result = { entries: [], layout: null }; |
| |
| $(this.entries).each(function () { |
| var entry = this; |
| var offsetStart = self.options.offsetStart; |
| var offsetEnd = self.options.offsetEnd; |
| var evaluatedString; |
| |
| |
| if (offsetStart && offsetEnd) { |
| if (index >= offsetStart && index <= offsetEnd) { |
| if (self.isRelevant(entry, result.entries)) { |
| evaluatedString = self.evaluateStringForEntry( |
| self.options.entryTemplate, entry |
| ); |
| |
| result.entries.push(evaluatedString); |
| } |
| } |
| } else { |
| |
| if (self.isRelevant(entry, result.entries)) { |
| evaluatedString = self.evaluateStringForEntry( |
| self.options.entryTemplate, entry |
| ); |
| |
| result.entries.push(evaluatedString); |
| } |
| } |
| }); |
| |
| if (!!this.options.entryTemplate) { |
| |
| result.layout = this.wrapContent( |
| this.options.layoutTemplate.replace('{entries}', '<entries></entries>') |
| ); |
| } else { |
| |
| result.layout = this.wrapContent('<div><entries></entries></div>'); |
| } |
| |
| return result; |
| }; |
| |
| RSS.prototype.wrapContent = function (content) { |
| if (($.trim(content).indexOf('<') !== 0)) { |
| |
| return $('<div>' + content + '</div>'); |
| } else { |
| |
| return $(content); |
| } |
| }; |
| |
| RSS.prototype.applyEffect = function ($element, effect, callback) { |
| var self = this; |
| |
| switch (effect) { |
| case 'slide': |
| $element.slideDown('slow', callback); |
| break; |
| case 'slideFast': |
| $element.slideDown(callback); |
| break; |
| case 'slideSynced': |
| self.effectQueue.push({ element: $element, effect: 'slide' }); |
| break; |
| case 'slideFastSynced': |
| self.effectQueue.push({ element: $element, effect: 'slideFast' }); |
| break; |
| } |
| }; |
| |
| RSS.prototype.executeEffectQueue = function (callback) { |
| var self = this; |
| |
| this.effectQueue.reverse(); |
| |
| var executeEffectQueueItem = function () { |
| var item = self.effectQueue.pop(); |
| |
| if (item) { |
| self.applyEffect(item.element, item.effect, executeEffectQueueItem); |
| } else if (callback) { |
| callback(); |
| } |
| }; |
| |
| executeEffectQueueItem(); |
| }; |
| |
| RSS.prototype.evaluateStringForEntry = function (string, entry) { |
| var result = string; |
| var self = this; |
| |
| $(string.match(/(\{.*?\})/g)).each(function () { |
| var token = this.toString(); |
| |
| result = result.replace(token, self.getValueForToken(token, entry)); |
| }); |
| |
| return result; |
| }; |
| |
| RSS.prototype.isRelevant = function (entry, entries) { |
| var tokenMap = this.getTokenMap(entry); |
| |
| if (this.options.filter) { |
| if (this.options.filterLimit && (this.options.filterLimit === entries.length)) { |
| return false; |
| } else { |
| return this.options.filter(entry, tokenMap); |
| } |
| } else { |
| return true; |
| } |
| }; |
| |
| RSS.prototype.getFormattedDate = function (dateString) { |
| |
| if (this.options.dateFormatFunction) { |
| return this.options.dateFormatFunction(dateString); |
| } else if (typeof moment !== 'undefined') { |
| |
| |
| var date = moment(new Date(dateString)); |
| |
| if (date.locale) { |
| date = date.locale(this.options.dateLocale); |
| } else { |
| date = date.lang(this.options.dateLocale); |
| } |
| |
| return date.format(this.options.dateFormat); |
| } else { |
| |
| return dateString; |
| } |
| }; |
| |
| RSS.prototype.getTokenMap = function (entry) { |
| if (!this.feedTokens) { |
| var feed = JSON.parse(JSON.stringify(this.feed)); |
| |
| delete feed.entries; |
| this.feedTokens = feed; |
| } |
| |
| return $.extend({ |
| feed: this.feedTokens, |
| url: entry.link, |
| author: entry.author, |
| date: this.getFormattedDate(entry.publishedDate), |
| title: entry.title, |
| body: entry.content, |
| shortBody: entry.contentSnippet, |
| |
| bodyPlain: (function (entry) { |
| var result = entry.content |
| .replace(/<script[\\r\\\s\S]*<\/script>/mgi, '') |
| .replace(/<\/?[^>]+>/gi, ''); |
| |
| for (var i = 0; i < RSS.htmlTags.length; i++) { |
| result = result.replace(new RegExp('<' + RSS.htmlTags[i], 'gi'), ''); |
| } |
| |
| return result; |
| })(entry), |
| |
| shortBodyPlain: entry.contentSnippet.replace(/<\/?[^>]+>/gi, ''), |
| index: $.inArray(entry, this.entries), |
| totalEntries: this.entries.length, |
| |
| teaserImage: (function (entry) { |
| try { |
| return entry.content.match(/(<img.*?>)/gi)[0]; |
| } |
| catch (e) { |
| return ''; |
| } |
| })(entry), |
| |
| teaserImageUrl: (function (entry) { |
| try { |
| return entry.content.match(/(<img.*?>)/gi)[0].match(/src="(.*?)"/)[1]; |
| } |
| catch (e) { |
| return ''; |
| } |
| })(entry) |
| }, this.options.tokens); |
| }; |
| |
| RSS.prototype.getValueForToken = function (_token, entry) { |
| var tokenMap = this.getTokenMap(entry); |
| var token = _token.replace(/[\{\}]/g, ''); |
| var result = tokenMap[token]; |
| |
| if (typeof result !== 'undefined') { |
| return ((typeof result === 'function') ? result(entry, tokenMap) : result); |
| } else { |
| throw new Error('Unknown token: ' + _token + ', url:' + this.url); |
| } |
| }; |
| |
| $.fn.rss = function (url, options, callback) { |
| new RSS(this, url, options, callback).render(); |
| return this; |
| }; |
| })(jQuery); |