1/*! 2 * jQuery RSS plugin â XML edition 3 * Fetches and parses raw RSS 2.0 / Atom XML instead of going through a JSON(P) proxy. 4 * Public API is unchanged: $(selector).rssfeed(url, options, callback) 5 */ 6!function (t) { 7 "use strict"; 8 var e = function (e, n, o, i) { 9 10 n = n.replace("http://", ""); 11 n = n.replace("https://", ""); 12 13 this.target = e; 14 this.url = n; 15 this.html = []; 16 this.effectQueue = []; 17 this.options = t.extend( 18 { 19 ssl: !0, 20 // Set host to a CORS-enabled XML proxy, or leave null to request the feed URL directly. 21 host: this.url, 22 limit: null, 23 key: null, 24 layoutTemplate: '<div class="rssBody"><ul class="rssFeed">{entries}</ul></div>', 25 entryTemplate: '<li class="rssRow"><h4><a href="{url}">{title}</a></h4><p>{shortBodyPlain}...</p></li>', 26 tokens: {}, 27 outputMode: "xml", 28 dateFormat: "dddd MMM Do", 29 dateLocale: "en", 30 effect: "show", 31 offsetStart: !1, 32 offsetEnd: !1, 33 error: function () { 34 console.log("jQuery RSS: url doesn't link to a valid RSS/Atom feed"); 35 }, 36 onData: function () {}, 37 success: function () {} 38 }, 39 o || {} 40 ); 41 this.callback = i || this.options.success; 42 }; 43 44 e.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"]; 45 46 // --- Loading: request XML instead of JSONP --------------------------------- 47 e.prototype.load = function (e) { 48 var url; 49 if (this.options.host) { 50 // Route through a proxy/host that returns the raw feed XML. 51 url = "http" + (this.options.ssl ? "s" : "") + "://" + this.options.host + "?q=" + encodeURIComponent(this.url); 52 if (null !== this.options.limit) url += "&num=" + this.options.limit; 53 if (null !== this.options.key) url += "&key=" + this.options.key; 54 } else { 55 // Fetch the feed directly (requires the feed to be same-origin or CORS-enabled). 56 url = this.url; 57 } 58 59 t.ajax({ 60 url: url, 61 type: "GET", 62 dataType: "xml", 63 success: e, 64 error: t.proxy(function () { 65 this.entries = []; 66 this.feed = null; 67 this.options.error.call(this); 68 if (this.effectQueue.length === 0 && t.isFunction(this.callback)) this.callback.call(this); 69 }, this) 70 }); 71 }; 72 73 // --- XML parsing helpers ---------------------------------------------------- 74 e.prototype.getText = function (parent, tagName) { 75 if (!parent) return ""; 76 var nodes = parent.getElementsByTagName(tagName); 77 if (!nodes || nodes.length === 0) return ""; 78 return (nodes[0].textContent || "").trim(); 79 }; 80 81 e.prototype.parseXML = function (xmlDoc) { 82 var self = this; 83 var feed = { title: "", link: "", description: "", entries: [] }; 84 85 if (!xmlDoc) return feed; 86 87 var channel = xmlDoc.getElementsByTagName("channel")[0]; 88 var isAtom = !channel && xmlDoc.getElementsByTagName("feed").length > 0; 89 90 if (isAtom) { 91 var atomRoot = xmlDoc.getElementsByTagName("feed")[0]; 92 feed.title = self.getText(atomRoot, "title"); 93 feed.description = self.getText(atomRoot, "subtitle"); 94 // Atom <link> uses href attribute. 95 var feedLinks = atomRoot.getElementsByTagName("link"); 96 if (feedLinks.length) feed.link = feedLinks[0].getAttribute("href") || ""; 97 98 var atomEntries = xmlDoc.getElementsByTagName("entry"); 99 for (var i = 0; i < atomEntries.length; i++) {
100 feed.entries.push(self.parseAtomEntry(atomEntries[i])); 101 } 102 } else if (channel) { 103 feed.title = self.getText(channel, "title"); 104 feed.link = self.getText(channel, "link"); 105 feed.description = self.getText(channel, "description"); 106 107 var items = channel.getElementsByTagName("item"); 108 for (var j = 0; j < items.length; j++) { 109 feed.entries.push(self.parseRssItem(items[j])); 110 } 111 } 112 113 return feed; 114 }; 115 116 e.prototype.parseRssItem = function (item) { 117 var content = 118 this.getText(item, "encoded") || // content:encoded 119 this.getText(item, "description"); 120 return { 121 title: this.getText(item, "title"), 122 link: this.getText(item, "link"), 123 author: this.getText(item, "creator") || this.getText(item, "author"), 124 publishedDate: this.getText(item, "pubDate") || this.getText(item, "date"), 125 content: content, 126 contentSnippet: content.replace(/<\/?[^>]+>/gi, "") 127 }; 128 }; 129 130 e.prototype.parseAtomEntry = function (entry) { 131 var content = this.getText(entry, "content") || this.getText(entry, "summary"); 132 var link = ""; 133 var links = entry.getElementsByTagName("link"); 134 if (links.length) link = links[0].getAttribute("href") || ""; 135 136 // Atom author is nested: <author><name>...</name></author> 137 var author = ""; 138 var authorNode = entry.getElementsByTagName("author")[0]; 139 if (authorNode) author = this.getText(authorNode, "name"); 140 141 return { 142 title: this.getText(entry, "title"), 143 link: link, 144 author: author, 145 publishedDate: this.getText(entry, "published") || this.getText(entry, "updated"), 146 content: content, 147 contentSnippet: content.replace(/<\/?[^>]+>/gi, "") 148 }; 149 }; 150 151 // --- Rendering -------------------------------------------------------------- 152 e.prototype.render = function () { 153 var e = this; 154 this.load(function (xmlDoc) { 155 try { 156 e.feed = e.parseXML(xmlDoc); 157 e.entries = e.feed.entries; 158 if (!e.feed || e.entries.length === 0) { 159 // Still allow empty render below, but a totally unparseable doc -> error. 160 if (!e.feed) throw new Error("no feed"); 161 } 162 } catch (t) { 163 e.entries = []; 164 e.feed = null; 165 return e.options.error.call(e); 166 } 167 168 var o = e.generateHTMLForEntries(); 169 e.target.append(o.layout); 170 171 if (0 !== o.entries.length) { 172 t.isFunction(e.options.onData) && e.options.onData.call(e); 173 var i = t(o.layout).is("entries") ? o.layout : t("entries", o.layout); 174 e.appendEntriesAndApplyEffects(i, o.entries); 175 } 176 177 e.effectQueue.length > 0 178 ? e.executeEffectQueue(e.callback) 179 : t.isFunction(e.callback) && e.callback.call(e); 180 }); 181 }; 182 183 e.prototype.appendEntriesAndApplyEffects = function (e, n) { 184 var o = this; 185 t.each(n, function (t, n) { 186 var i = o.wrapContent(n); 187 "show" === o.options.effect 188 ? e.before(i) 189 : (i.css({ display: "none" }), e.before(i), o.applyEffect(i, o.options.effect)); 190 }); 191 e.remove(); 192 }; 193 194 e.prototype.generateHTMLForEntries = function () { 195 var e = this, 196 n = { entries: [], layout: null }, 197 // The feed itself returns every item, so honour `limit` here on the client. 198 l = null == e.options.limit ? null : parseInt(e.options.limit, 10); 199 if (null !== l && (isNaN(l) || l < 0)) l = null; 200 t(this.entries).each(function (index) { 201 var t, 202 o = this, 203 i = e.options.offsetStart, 204 s = e.options.offsetEnd; 205 if (null !== l && n.entries.length >= l) return !1; 206 i && s 207 ? index >= i && 208 index <= s && 209 e.isRelevant(o, n.entries) && 210 ((t = e.evaluateStringForEntry(e.options.entryTemplate, o)), n.entries.push(t)) 211 : e.isRelevant(o, n.entries) && 212 ((t = e.evaluateStringForEntry(e.options.entryTemplate, o)), n.entries.push(t)); 213 }); 214 this.options.entryTemplate 215 ? (n.layout = this.wrapContent(this.options.layoutTemplate.replace("{entries}", "<entries></entries>"))) 216 : (n.layout = this.wrapContent("<div><entries></entries></div>")); 217 return n; 218 }; 219 220 e.prototype.wrapContent = function (e) { 221 return t(0 !== t.trim(e).indexOf("<") ? "<div>" + e + "</div>" : e); 222 }; 223 224 e.prototype.applyEffect = function (t, e, n) { 225 var o = this; 226 switch (e) { 227 case "slide": 228 t.slideDown("slow", n); 229 break; 230 case "slideFast": 231 t.slideDown(n); 232 break; 233 case "slideSynced": 234 o.effectQueue.push({ element: t, effect: "slide" }); 235 break; 236 case "slideFastSynced": 237 o.effectQueue.push({ element: t, effect: "slideFast" }); 238 } 239 }; 240 241 e.prototype.executeEffectQueue = function (t) { 242 var e = this; 243 this.effectQueue.reverse(); 244 var n = function () { 245 var o = e.effectQueue.pop(); 246 o ? e.applyEffect(o.element, o.effect, n) : t && t(); 247 }; 248 n(); 249 }; 250 251 e.prototype.evaluateStringForEntry = function (e, n) { 252 var o = e, 253 i = this; 254 t(e.match(/(\{.*?\})/g)).each(function () { 255 var t = this.toString(); 256 o = o.replace(t, i.getValueForToken(t, n)); 257 }); 258 return o; 259 }; 260 261 e.prototype.isRelevant = function (t, e) { 262 var n = this.getTokenMap(t); 263 return ( 264 !this.options.filter || 265 ((!this.options.filterLimit || this.options.filterLimit !== e.length) && this.options.filter(t, n)) 266 ); 267 }; 268 269 e.prototype.getFormattedDate = function (t) { 270 if (this.options.dateFormatFunction) return this.options.dateFormatFunction(t); 271 if ("undefined" != typeof moment) { 272 var e = moment(new Date(t)); 273 return (e = e.locale ? e.locale(this.options.dateLocale) : e.lang(this.options.dateLocale)).format( 274 this.options.dateFormat 275 ); 276 } 277 return t; 278 }; 279 280 e.prototype.getTokenMap = function (n) { 281 if (!this.feedTokens) { 282 var o = JSON.parse(JSON.stringify(this.feed)); 283 delete o.entries; 284 this.feedTokens = o; 285 } 286 return t.extend( 287 { 288 feed: this.feedTokens, 289 url: n.link, 290 author: n.author, 291 date: this.getFormattedDate(n.publishedDate), 292 title: n.title, 293 body: n.content, 294 shortBody: n.contentSnippet, 295 bodyPlain: (function (t) { 296 for ( 297 var n = t.content.replace(/<script[\r\s\S]*<\/script>/gim, "").replace(/<\/?[^>]+>/gi, ""), o = 0; 298 o < e.htmlTags.length; 299 o++ 300 ) 301 n = n.replace(new RegExp("<" + e.htmlTags[o], "gi"), ""); 302 return n; 303 })(n), 304 shortBodyPlain: n.contentSnippet.replace(/<\/?[^>]+>/gi, ""), 305 index: t.inArray(n, this.entries), 306 totalEntries: this.entries.length, 307 teaserImage: (function (t) { 308 try { 309 return t.content.match(/(<img.*?>)/gi)[0]; 310 } catch (t) { 311 return ""; 312 } 313 })(n), 314 teaserImageUrl: (function (t) { 315 try { 316 return t.content.match(/(<img.*?>)/gi)[0].match(/src="(.*?)"/)[1]; 317 } catch (t) { 318 return ""; 319 } 320 })(n) 321 }, 322 this.options.tokens 323 ); 324 }; 325 326 e.prototype.getValueForToken = function (t, e) { 327 var n = this.getTokenMap(e), 328 o = n[t.replace(/[\{\}]/g, "")]; 329 if (void 0 !== o) return "function" == typeof o ? o(e, n) : o; 330 throw new Error("Unknown token: " + t + ", url:" + this.url); 331 }; 332 333 t.fn.rssfeed = function (t, n, o) { 334 return new e(this, t, n, o).render(), this; 335 }; 336}(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.