├── .eslintrc ├── .gitignore ├── .travis.yml ├── utils ├── sync-version.js └── bundle.js ├── examples ├── custom-dates.html ├── ezypay.html ├── visitlakecounty.html ├── effects.html ├── zotero.html ├── multi-feed.html └── enclosure.html ├── src └── jquery.rss.js ├── MIT-LICENSE ├── index.html ├── package.json ├── test ├── fixtures │ ├── echojs.rss.json │ ├── contentful.rss.json │ ├── echojs.rss.xml │ └── contentful.rss.xml └── jquery.rss.test.js ├── CHANGELOG.md ├── dist └── jquery.rss.min.js └── README.md /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "eslint:recommended" 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | test.html 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | script: 2 | - "npm test" 3 | 4 | notifications: 5 | email: 6 | - sascha@depold.com 7 | 8 | language: node_js 9 | 10 | node_js: 11 | - 8 12 | -------------------------------------------------------------------------------- /utils/sync-version.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const { version } = require('../package.json'); 3 | const sourcePath = __dirname + '/../src/jquery.rss.js'; 4 | 5 | const lines = fs.readFileSync(sourcePath).toString().split('\n'); 6 | const updatedLines = lines.map((line) => { 7 | if (!line.includes('// Synced version')) { 8 | return line; 9 | } 10 | 11 | return ` this.version = '${version}'; // Synced version` 12 | }); 13 | 14 | fs.writeFileSync(sourcePath, updatedLines.join('\n')); -------------------------------------------------------------------------------- /utils/bundle.js: -------------------------------------------------------------------------------- 1 | const { rollup } = require("rollup"); 2 | const { terser } = require("rollup-plugin-terser"); 3 | const resolve = require('rollup-plugin-node-resolve'); 4 | const { writeFileSync } = require('fs'); 5 | 6 | (async () => { 7 | const bundle = await rollup({ 8 | input: __dirname + "/../src/jquery.rss.js", 9 | plugins: [resolve(), terser()] 10 | }); 11 | 12 | const { output } = await bundle.generate({ format: 'iife', name: 'RSS' }); 13 | const outputPath = `${__dirname}/../dist/jquery.rss.min.js`; 14 | 15 | writeFileSync(outputPath, output[0].code); 16 | console.log(`${outputPath} was updated!`); 17 | })(); -------------------------------------------------------------------------------- /examples/custom-dates.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | jquery.rss accordion example 5 | 6 | 7 | 8 | 9 | 10 | 18 | 19 | 20 |
21 |

jquery.rss example

22 |
23 |
24 | 25 | 26 | -------------------------------------------------------------------------------- /examples/ezypay.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | jquery.rss accordion example 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 20 | 21 | 22 |
23 |

jquery.rss example

24 |
25 |
26 | 27 | 28 | -------------------------------------------------------------------------------- /examples/visitlakecounty.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | jquery.rss accordion example 5 | 6 | 7 | 8 | 9 | 10 | 18 | 19 | 20 |
21 |

jquery.rss example

22 |
23 |
24 | 25 | 26 | -------------------------------------------------------------------------------- /src/jquery.rss.js: -------------------------------------------------------------------------------- 1 | import RSS from "vanilla-rss"; 2 | 3 | (function($) { 4 | $.fn.rss = function(url, options = {}, callback) { 5 | const rss = new RSS(this, url, { 6 | ...options, 7 | fetchFeed: apiUrl => { 8 | return new Promise((resolve, reject) => { 9 | $.ajax({ 10 | dataType: "json", 11 | url: apiUrl, 12 | success: resolve, 13 | error: reject 14 | }); 15 | }); 16 | } 17 | }); 18 | 19 | rss.render().then( 20 | (...args) => { 21 | callback && callback(...args); 22 | options && options.success && options.success(...args); 23 | }, 24 | (...args) => { 25 | options && options.error && options.error(...args); 26 | } 27 | ); 28 | 29 | return this; // Implement chaining 30 | }; 31 | })(jQuery); 32 | -------------------------------------------------------------------------------- /MIT-LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011 Sascha Depold 2 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 3 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 4 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 5 | -------------------------------------------------------------------------------- /examples/effects.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | jquery.rss accordion example 5 | 6 | 11 | 12 | 13 | 14 | 15 | 30 | 31 | 36 | 37 | 38 |
39 |

jquery.rss example

40 |
41 |
42 | 43 | 44 | -------------------------------------------------------------------------------- /examples/zotero.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | jquery.rss zotero example 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 27 | 28 | 29 |
30 |

jquery.rss zotero example

31 |
32 |
33 | 34 | 35 | -------------------------------------------------------------------------------- /examples/multi-feed.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | jquery.rss accordion example 5 | 6 | 11 | 12 | 13 | 14 | 15 | 35 | 36 | 41 | 42 | 43 |
44 |

jquery.rss example

45 |
46 |
47 | 48 | 49 | -------------------------------------------------------------------------------- /examples/enclosure.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | jquery.rss zotero example 5 | 6 | 11 | 12 | 13 | 14 | 15 | 16 | 44 | 45 | 46 |
47 |

jquery.rss zotero example

48 |
49 |
50 | 51 | 52 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | jquery.rss accordion example 5 | 6 | 11 | 12 | 13 | 14 | 15 | 30 | 31 | 36 | 37 | 38 |
39 |

jquery.rss example

40 | 41 |

Examples:

42 | 50 | 51 |
52 |
53 | 54 | 55 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": "Sascha Depold (http://depold.com)", 3 | "name": "jquery-rss", 4 | "description": "An easy-to-use rss plugin for jquery with templating.", 5 | "version": "4.3.0", 6 | "homepage": "https://github.com/sdepold/jquery-rss", 7 | "keywords": [ 8 | "rss", 9 | "jquery", 10 | "ender" 11 | ], 12 | "ender": "noop", 13 | "repository": { 14 | "type": "git", 15 | "url": "git://github.com/sdepold/jquery-rss.git" 16 | }, 17 | "contributors": [ 18 | { 19 | "name": "Sascha Depold", 20 | "email": "sascha@depold.com" 21 | }, 22 | { 23 | "name": "Steffen Schröder", 24 | "email": "steffen@schroeder-blog.de" 25 | } 26 | ], 27 | "main": "src/jquery.rss.js", 28 | "scripts": { 29 | "test": "npm run bundle && mocha test/jquery.rss.test.js -t 10000", 30 | "bundle": "node utils/bundle", 31 | "lint": "eslint src/**/*.js spec/**/*.js", 32 | "prepack": "npm run version:sync && npm run bundle", 33 | "version:sync": "node utils/sync-version", 34 | "serve": "static &", 35 | "watch": "npm run serve && watch 'npm run bundle' ./src" 36 | }, 37 | "engines": { 38 | "node": ">=v0.4.8" 39 | }, 40 | "dependencies": { 41 | "vanilla-rss": "^1.4.0" 42 | }, 43 | "devDependencies": { 44 | "chai": "^4.2.0", 45 | "eslint": "^6.4.0", 46 | "jquery": "^3.4.0", 47 | "jsdom": "^15.1.1", 48 | "mocha": "^6.2.0", 49 | "moment": "^2.14.1", 50 | "node-fetch": "^2.6.0", 51 | "node-static": "^0.7.11", 52 | "rollup": "^1.23.1", 53 | "rollup-plugin-node-resolve": "^5.2.0", 54 | "rollup-plugin-terser": "^5.1.2", 55 | "sinon": "^7.5.0", 56 | "watch": "^1.0.2" 57 | }, 58 | "license": "MIT" 59 | } 60 | -------------------------------------------------------------------------------- /test/fixtures/echojs.rss.json: -------------------------------------------------------------------------------- 1 | { 2 | "responseStatus": 200, 3 | "responseDetails": null, 4 | "responseData": { 5 | "feed": { 6 | "feedUrl": "http://www.echojs.com/rss", 7 | "title": "\nEcho JS\n", 8 | "link": "\nhttp://www.echojs.com\n", 9 | "description": "Description pending", 10 | "author": "", 11 | "entries": [ 12 | { 13 | "title": "\nReact Component npm package boilerplate with hooks, typescript, lint, jest and example hot reload\n", 14 | "link": "\nhttps://www.wolesblog.com/react-component-npm-package-boilerplate-with-hooks-typescript-lint-jest-and-example-part-1/\n", 15 | "content": "Comments", 16 | "contentSnippet": "Comments", 17 | "categories": [], 18 | "author": "" 19 | }, 20 | { 21 | "title": "\nProlog interpreter written in TypeScript\n", 22 | "link": "\nhttps://github.com/kkty/prolog\n", 23 | "content": "Comments", 24 | "contentSnippet": "Comments", 25 | "categories": [], 26 | "author": "" 27 | }, 28 | { 29 | "title": "\nJavascript library to parse text into flowcharts, sequence graphs, and more...\n", 30 | "link": "\nhttps://github.com/knsv/mermaid\n", 31 | "content": "Comments", 32 | "contentSnippet": "Comments", 33 | "categories": [], 34 | "author": "" 35 | }, 36 | { 37 | "title": "\nHow I Setup My VIM as Modern Text Editor\n", 38 | "link": "\nhttps://medium.com/@zafarsaleem/how-i-setup-my-vim-as-modern-text-editor-41a93ca4c7a8\n", 39 | "content": "Comments", 40 | "contentSnippet": "Comments", 41 | "categories": [], 42 | "author": "" 43 | } 44 | ] 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## Change Log 2 | All notable changes to this project will be documented in this file. 3 | 4 | ## 4.3.0 5 | ### Changed 6 | - Add support for sorting of entries (through vanilla-rss 1.4.0) 7 | 8 | ## 4.2.0 9 | ### Changed 10 | - Add support for multiple feed URLs (through vanilla-rss 1.3.0) 11 | 12 | ## 4.1.0 13 | ### Changed 14 | - Add support for custom feed encoding 15 | 16 | ## 4.0.0 17 | ### Changed 18 | - Replace business logic with [Vanilla RSS](https://github.com/sdepold/vanilla-rss) 19 | ### Removed 20 | - Support for effects (check the effects example for an alternative approach) 21 | 22 | ## v3.2.1 23 | ### Fixed 24 | - Rendering of layoutTemplate `{entries}` 25 | 26 | ## v3.2.0 27 | ### Added 28 | - Possibility to specify the date locale via moment.js 29 | 30 | ## v3.1.0 31 | ### Changed 32 | - Re-added support for SSL 33 | 34 | ## v3.0.1 35 | ### Changed 36 | - Use www.feedrapp.info instead of feedrapp.info 37 | 38 | ## v3.0.0 39 | ### Changed 40 | - Replace Google Feed API with [feedr](https://github.com/sdepold/feedr) 41 | 42 | ## v2.0.0 43 | ### Changed 44 | - moment.js is now optional 45 | - Please note that the format of dates might change when moment.js is available and no `dateFormat` option is specified. In that scenario all dates will be transformed to the format `dddd MMM Do`. 46 | 47 | ## v1.5.1 48 | ### Fixed 49 | - moment.js deprecation warning 50 | 51 | ## v1.5.0 52 | ### Added 53 | - `onData` callback which gets triggered after receiving the data but before the rendering. 54 | 55 | ## v1.4.0 56 | ### Added 57 | - Pass the feeds meta data to the tokens object. 58 | 59 | ## v1.3.0 60 | ### Added 61 | - Error and success callback. (thanks to eliten00b) 62 | 63 | ### Fixed 64 | - forEach loop. (thanks to aegisrunestone) 65 | 66 | ## v1.2.0 67 | ### Added 68 | - Possibility to define effects for the appearance of entries 69 | 70 | ## v1.1.0 71 | ### Added 72 | - XSS protection 73 | 74 | ### Changed 75 | - Switched to busterjs for tests 76 | - Implemented tests for XSS protection 77 | 78 | ## v1.0.0 79 | ### Changed 80 | - Complete test coverage with mocha 81 | 82 | ## v0.4.0 83 | ### Added 84 | - Possibility to define the output method of google request 85 | 86 | ### Changed 87 | - Separate layout template from entry template (thanks to ChaosSteffen) 88 | 89 | ## v0.3.0 90 | ### Added 91 | - Callback, which is triggered after rendering of all entries (thanks to cm0s) 92 | 93 | ### Changed 94 | - Evaluate token map before passing it to custom token functions 95 | - Moved minified version into `dist` folder (thanks to markrambow) 96 | 97 | ## v0.2.2 98 | ### Fixed 99 | - Array#indexOf IE bug 100 | 101 | ## v0.2.1 102 | ### Fixed 103 | - Catch failures while extracting images 104 | 105 | ## v0.2.0 106 | ### Added 107 | - The tokens `index` and `totalEntries` 108 | - Preparation for jasmine tests 109 | 110 | ## v0.1.1 111 | ### Added 112 | - Entry filtering 113 | -------------------------------------------------------------------------------- /test/fixtures/contentful.rss.json: -------------------------------------------------------------------------------- 1 | { 2 | "responseStatus": 200, 3 | "responseDetails": null, 4 | "responseData": { 5 | "feed": { 6 | "feedUrl": "https://www.contentful.com/blog/feed.xml", 7 | "title": "Contentful - Blog", 8 | "link": "https://www.contentful.com", 9 | "description": "Contentful gives you an API-first, cloud-based platform to power your sites and apps, allowing you to create first-class user experiences. Stop burying your content in a CMS, empower it with a content infrastructure.", 10 | "author": "", 11 | "entries": [ 12 | { 13 | "title": "Why I’m going to the Ada Lovelace Festival (and you should, too!) ", 14 | "link": "https://www.contentful.com/blog/2019/09/25/you-should-go-to-ada-lovelace/", 15 | "content": "Ada Lovelace was a badass. She wrote one of the first computer programs when almost no women worked in tech — in 1842, to be precise. On Oct. 24–25, the Ada Lovelace Festival will celebrate her contributions and those of women leading tech today with talks, workshops and social activities.\n\nThis year’s event focuses on the topic of ownership. Speakers from leading enterprises — such as Volkswagen, Accenture and SAP — will engage with artists, activists and government officials to discuss who gets to own the future of the digital world, and how more people can be involved.\n\nI’m thrilled to attend this year. Here are a few reasons why.\n", 16 | "contentSnippet": "Ada Lovelace was a badass. She wrote one of the first computer programs when almost no women worked in tech — in 1842, t", 17 | "publishedDate": "2019-09-25T14:00:00.000Z", 18 | "categories": [], 19 | "author": "" 20 | }, 21 | { 22 | "title": "Keeping it cool: How one engineer uses Contentful to control a fridge in Argentina", 23 | "link": "https://www.contentful.com/blog/2019/09/19/contentful-controls-fridge-argentina/", 24 | "content": "We often talk about how Contentful can do anything and everything. It’s flexibility enables you to design your own content models and deliver content to any channel. But what are some of the very edge cases of how to use Contentful? In this post, I’ll describe one unexpected way I used Contentful to solve a very real problem for my father.\n", 25 | "contentSnippet": "We often talk about how Contentful can do anything and everything. It’s flexibility enables you to design your own conte", 26 | "publishedDate": "2019-09-19T10:00:00.000Z", 27 | "categories": [], 28 | "author": "" 29 | }, 30 | { 31 | "title": "Experimentation is now baked into Contentful with new Optimizely app", 32 | "link": "https://www.contentful.com/blog/2019/09/12/experimentation-in-contentful-new-optimizely-app/", 33 | "content": "If you’re a marketer, or you build things for marketers, you know that experimentation is everything. It can help you deliver faster, and can base your decisions on what’s actually bringing you results. Now you can run these experiments on structured content: From today, Optimizely is an officially supported third-party app on Contentful. ", 34 | "contentSnippet": "If you’re a marketer, or you build things for marketers, you know that experimentation is everything. It can help you de", 35 | "publishedDate": "2019-09-12T13:00:00.000Z", 36 | "categories": [], 37 | "author": "" 38 | }, 39 | { 40 | "title": "Turn your sidebar into a sensitive language check, a preview tool, and more with sidebar extensions ", 41 | "link": "https://www.contentful.com/blog/2019/09/11/sidebar-extensions-release-post/", 42 | "content": "The best way to adapt Contentful to your business or content processes is to use our UI extensions. It’s how you can customize your editing experience in the Contentful web app. One of these is sidebar extensions, which allows you to customize your sidebar to create a workspace with the controls you need. ", 43 | "contentSnippet": "The best way to adapt Contentful to your business or content processes is to use our UI extensions. It’s how you can cus", 44 | "publishedDate": "2019-09-11T12:00:00.000Z", 45 | "categories": [], 46 | "author": "" 47 | } 48 | ] 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /dist/jquery.rss.min.js: -------------------------------------------------------------------------------- 1 | !function(){"use strict";const t=["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"];class e{constructor(){this.topics={},this.hop=this.topics.hasOwnProperty}on(t,e){this.hop.call(this.topics,t)||(this.topics[t]=[]);const i=this.topics[t].push(e)-1;return{remove:()=>{this.topics[t].splice(i,1)}}}emit(t,e={}){return this.hop.call(this.topics,t)?this.topics[t].forEach(t=>t(e)):this}}function i(t){let e=document.createElement("template");return e.innerHTML=t.trim(),e.content.firstElementChild}class s{constructor(t,i,s={}){this.version="1.4.0",this.target=t,this.urls=[].concat(i),this.html=[],this.options={ssl:!0,host:"www.feedrapp.info",support:!0,limit:null,key:null,layoutTemplate:"",entryTemplate:'
  • [{author}@{date}] {title}
    {shortBodyPlain}
  • ',tokens:{},outputMode:"json",dateFormat:"dddd MMM Do",dateLocale:"en",offsetStart:!1,offsetEnd:!1,fetchFeed:null,encoding:null,...s},this.events=new e}on(t,e){return this.events.on(`vanilla-rss/${t}`,e),this}render(){return new Promise(async(t,e)=>{try{const t=await this._load();this.feed=t.responseData.feed,this.entries=t.responseData.feed.entries}catch(t){return this.entries=[],this.feed=null,e(t)}const i=this._generateHTMLForEntries();if(this.target.append(i.layout),0!==i.entries.length){this.events.emit("vanilla-rss/data",{rss:this,feed:this.feed,entries:this.entries});const t=function(t,e){return t.tagName.toLowerCase()===e.toLowerCase()}(i.layout,"entries")?i.layout:i.layout.querySelector("entries");this._appendEntries(t,i.entries)}t()})}_appendEntries(t,e){e.forEach((e,i)=>{var s=this._wrapContent(e);t.insertAdjacentHTML("beforebegin",s.outerHTML)}),t.remove()}_wrapContent(t){return 0!==t.trim().indexOf("<")?i(`
    ${t}
    `):i(t)}_load(){const t=`${`http${this.options.ssl?"s":""}`}://${this.options.host}`,e={support:this.options.support,version:this.version,q:this.urls.map(t=>encodeURIComponent(t)).join(",")};this.options.offsetStart&&this.options.offsetEnd&&(this.options.limit=this.options.offsetEnd),null!==this.options.limit&&(e.num=this.options.limit),null!==this.options.key&&(e.key=this.options.key),null!==this.options.encoding&&(e.encoding=this.options.encoding),this.options.order&&(e.order=this.options.order);const i=`${t}?${Object.keys(e).map(t=>`${t}=${e[t]}`).join("&")}`;return this._fetchFeed(i)}async _fetchFeed(t){if(this.options.fetchFeed)return await this.options.fetchFeed(t);const e=await fetch(t,{headers:{"Content-Type":"application/json"}});return await e.json()}_generateHTMLForEntries(){const t={entries:[],layout:null};return this.entries.forEach((e,i)=>{const s=this.options.offsetStart,n=this.options.offsetEnd;let o;s&&n?i>=s&&i<=n&&this._isRelevant(e,t.entries)&&(o=this._evaluateStringForEntry(this.options.entryTemplate,e),t.entries.push(o)):this._isRelevant(e,t.entries)&&(o=this._evaluateStringForEntry(this.options.entryTemplate,e),t.entries.push(o))}),this.options.entryTemplate?t.layout=this._wrapContent(this.options.layoutTemplate.replace("{entries}","")):t.layout=this._wrapContent("
    "),t}_isRelevant(t,e){const i=this._getTokenMap(t);return!this.options.filter||(!this.options.filterLimit||this.options.filterLimit!==e.length)&&this.options.filter(t,i)}_evaluateStringForEntry(t,e){var i=t;return(t.match(/(\{.*?\})/g)||[]).forEach(t=>{i=i.replace(t,this._getValueForToken(t,e))}),i}_getFormattedDate(t){if(this.options.dateFormatFunction)return this.options.dateFormatFunction(t);if("undefined"!=typeof moment){var e=moment(new Date(t));return(e=e.locale?e.locale(this.options.dateLocale):e.lang(this.options.dateLocale)).format(this.options.dateFormat)}return t}_getTokenMap(e){if(!this.feedTokens){var i=JSON.parse(JSON.stringify(this.feed));delete i.entries,this.feedTokens=i}return{feed:this.feedTokens,url:e.link,author:e.author,date:this._getFormattedDate(e.publishedDate),title:e.title,body:e.content,shortBody:e.contentSnippet,bodyPlain:function(e){for(var i=e.content.replace(//gim,"").replace(/<\/?[^>]+>/gi,""),s=0;s]+>/gi,""),index:this.entries.indexOf(e),totalEntries:this.entries.length,teaserImage:function(t){try{return t.content.match(/()/gi)[0]}catch(t){return""}}(e),teaserImageUrl:function(t){try{return t.content.match(/()/gi)[0].match(/src=["'](.*?)["']/)[1]}catch(t){return""}}(e),...this.options.tokens}}_getValueForToken(t,e){var i=this._getTokenMap(e),s=i[t.replace(/[\{\}]/g,"")];if(void 0!==s)return"function"==typeof s?s(e,i):s;throw new Error("Unknown token: "+t+", url:"+this.url)}}var n;(n=jQuery).fn.rss=function(t,e={},i){return new s(this,t,{...e,fetchFeed:t=>new Promise((e,i)=>{n.ajax({dataType:"json",url:t,success:e,error:i})})}).render().then((...t)=>{i&&i(...t),e&&e.success&&e.success(...t)},(...t)=>{e&&e.error&&e.error(...t)}),this}}(); 2 | -------------------------------------------------------------------------------- /test/jquery.rss.test.js: -------------------------------------------------------------------------------- 1 | const { JSDOM } = require("jsdom"); 2 | const sampleFeed = require("fs") 3 | .readFileSync(__dirname + "/fixtures/contentful.rss.xml") 4 | .toString(); 5 | const sampleFeedParsed = require("./fixtures/contentful.rss.json"); 6 | const { expect } = require("chai"); 7 | const fetch = require("node-fetch"); 8 | const moment = (global.moment = require("moment")); 9 | const { stub } = require("sinon"); 10 | const { version } = require("../package.json"); 11 | 12 | describe("jquery.rss", () => { 13 | let $, element, originalAjax; 14 | 15 | const feedUrl = "https://www.contentful.com/blog/feed.xml"; 16 | const fakeGetJson = content => { 17 | originalAjax = $.ajax; 18 | 19 | $.ajax = function({ url, success }) { 20 | success({ 21 | responseData: { 22 | feed: { 23 | entries: [ 24 | { 25 | content: content, 26 | contentSnippet: content 27 | } 28 | ] 29 | } 30 | } 31 | }); 32 | }; 33 | }; 34 | 35 | before(() => { 36 | const { window, document } = new JSDOM(``); 37 | 38 | global.window = window; 39 | global.document = window.document; 40 | $ = global.jQuery = require("jquery"); 41 | 42 | require("../dist/jquery.rss.min"); 43 | }); 44 | 45 | beforeEach(() => { 46 | element = $("
    ").appendTo($("body")); 47 | }); 48 | 49 | afterEach(() => { 50 | if (typeof originalAjax === "function") { 51 | $.ajax = originalAjax; 52 | originalAjax = null; 53 | } 54 | }); 55 | 56 | it("supports multiple rss feeds", done => { 57 | originalAjax = $.ajax; 58 | $.ajax = function({ url, success }) { 59 | expect(url).to.include( 60 | "q=https%3A%2F%2Fwww.contentful.com%2Fblog%2Ffeed.xml,http%3A%2F%2Fwww.ebaytechblog.com%2Ffeed%2F" 61 | ); 62 | 63 | done(); 64 | }; 65 | 66 | var $container = element; 67 | 68 | $container.rss([ 69 | "https://www.contentful.com/blog/feed.xml", 70 | "http://www.ebaytechblog.com/feed/" 71 | ]); 72 | }); 73 | 74 | it("renders an unordered list by default", function(done) { 75 | var $container = element; 76 | 77 | $container.rss(feedUrl, {}, function() { 78 | var renderedContent = $container.html().replace(/\n/g, ""); 79 | 80 | expect(renderedContent).to.match(/
      .*<\/ul>/); 81 | done(); 82 | }); 83 | }); 84 | 85 | it("renders 2 list entries if limit is set to 2", function(done) { 86 | var $container = element; 87 | 88 | $container.rss( 89 | feedUrl, 90 | { 91 | limit: 2 92 | }, 93 | function() { 94 | expect($("li", $container).length).to.equal(2); 95 | done(); 96 | } 97 | ); 98 | }); 99 | 100 | it("renders the defined entry template", function(done) { 101 | var $container = element; 102 | 103 | $container.rss( 104 | feedUrl, 105 | { 106 | limit: 1, 107 | entryTemplate: "
    • foo
    • " 108 | }, 109 | function() { 110 | var renderedContent = $container 111 | .html() 112 | .split("\n") 113 | .map(function(s) { 114 | return s.trim(); 115 | }) 116 | .join("") 117 | .trim(); 118 | 119 | expect(renderedContent).to.match(/
      • foo<\/li><\/ul>/); 120 | done(); 121 | } 122 | ); 123 | }); 124 | 125 | it("renders the defined layout template", function(done) { 126 | var $container = element; 127 | 128 | $container.rss( 129 | feedUrl, 130 | { 131 | limit: 1, 132 | layoutTemplate: "foo
          {entries}
        bar" 133 | }, 134 | function() { 135 | var renderedContent = $container.html().replace(/\n/g, ""); 136 | 137 | expect(renderedContent).to.match(/foo
          .*<\/ul>/); 138 | done(); 139 | } 140 | ); 141 | }); 142 | 143 | it("supports custom tokens", function(done) { 144 | var $container = element; 145 | 146 | $container.rss( 147 | feedUrl, 148 | { 149 | limit: 1, 150 | entryTemplate: "
        • {myCustomStaticToken} {myCustomDynamicToken}
        • ", 151 | tokens: { 152 | myCustomStaticToken: "static", 153 | myCustomDynamicToken: function() { 154 | return "dynamic"; 155 | } 156 | } 157 | }, 158 | function() { 159 | var renderedContent = $container 160 | .html() 161 | .split("\n") 162 | .map(function(s) { 163 | return s.trim(); 164 | }) 165 | .join("") 166 | .trim(); 167 | 168 | expect(renderedContent).to.match( 169 | new RegExp("
          • static dynamic
          ") 170 | ); 171 | done(); 172 | } 173 | ); 174 | }); 175 | 176 | it("removes p-tags but not the content", function(done) { 177 | var $container = element; 178 | 179 | fakeGetJson("

          May the fourth be with you!

          "); 180 | 181 | $container.rss( 182 | feedUrl, 183 | { 184 | limit: 1, 185 | entryTemplate: "
        • {bodyPlain}
        • " 186 | }, 187 | function() { 188 | var renderedContent = $container 189 | .html() 190 | .split("\n") 191 | .map(function(s) { 192 | return s.trim(); 193 | }) 194 | .join("") 195 | .trim(); 196 | 197 | expect(renderedContent).to.match(/
          • .*<\/li><\/ul>/); 198 | done(); 199 | } 200 | ); 201 | }); 202 | 203 | it("calls the error callback if something went wrong", function(done) { 204 | element.rss("https://google.com", { 205 | error: function() { 206 | expect(1).to.equal(1); 207 | done(); 208 | } 209 | }); 210 | }); 211 | 212 | it("calls the success callback", function(done) { 213 | element.rss(feedUrl, { 214 | limit: 1, 215 | success: function() { 216 | expect(1).to.equal(1); 217 | done(); 218 | } 219 | }); 220 | }); 221 | 222 | it("renders the defined entry template in the layout template", function(done) { 223 | var $container = element; 224 | 225 | $container.rss( 226 | feedUrl, 227 | { 228 | limit: 1, 229 | entryTemplate: "
          • bazinga
          • ", 230 | layoutTemplate: "
            • topic
            • {entries}
            " 231 | }, 232 | function() { 233 | var renderedContent = $container.html().replace(/\n/g, ""); 234 | 235 | expect(renderedContent).to.equal( 236 | "
            • topic
            • bazinga
            " 237 | ); 238 | done(); 239 | } 240 | ); 241 | }); 242 | 243 | it("renders when layout template only contains the entries token", function(done) { 244 | var $container = $("").appendTo(element); 245 | 246 | $container.rss( 247 | feedUrl, 248 | { 249 | limit: 1, 250 | layoutTemplate: "{entries}", 251 | entryTemplate: "" 252 | }, 253 | function() { 254 | var renderedContent = $container[0].outerHTML.replace(/\n/g, ""); 255 | 256 | expect(renderedContent).to.match( 257 | /
            {title}
            " 252 | }); 253 | ``` 254 | 255 | ## Filtering 256 | 257 | The plugin also allows you to filter specific entries in order to only print them: 258 | 259 | ```javascript 260 | $("#foo").rss(url, { 261 | limit: 100, 262 | filterLimit: 10, 263 | filter: function(entry, tokens) { 264 | return tokens.title.indexOf("my filter") > -1; 265 | } 266 | }); 267 | ``` 268 | 269 | This will request 100 entries via the Feed API and renders the first 10 matching entries. 270 | 271 | ## Testing 272 | 273 | The test suite is using BusterJS. In order to successfully run the tests you will need [phantomjs](http://phantomjs.org/). 274 | If that is installed you only have to run `npm test`. 275 | 276 | ## Authors/Contributors 277 | 278 | - Sascha Depold ([Twitter](http://twitter.com/sdepold) | [Github](http://github.com/sdepold) | [Website](http://depold.com)) 279 | - Steffen Schröder ([Twitter](http://twitter.com/ChaosSteffen) | [Github](http://github.com/ChaosSteffen) | [Website](http://schroeder-blog.de)) 280 | -------------------------------------------------------------------------------- /test/fixtures/echojs.rss.xml: -------------------------------------------------------------------------------- 1 | 2 | Echo JS 3 | 4 | 5 | http://www.echojs.com 6 | 7 | Description pending 8 | React Component npm package boilerplate with hooks, typescript, lint, jest and example hot reload 9 | 10 | https://www.wolesblog.com/react-component-npm-package-boilerplate-with-hooks-typescript-lint-jest-and-example-part-1/ 11 | https://www.wolesblog.com/react-component-npm-package-boilerplate-with-hooks-typescript-lint-jest-and-example-part-1/ 12 | 13 | Comments]]> http://www.echojs.com/news/33780 14 | 15 | Prolog interpreter written in TypeScript 16 | 17 | https://github.com/kkty/prolog 18 | https://github.com/kkty/prolog 19 | 20 | Comments]]> http://www.echojs.com/news/33779 21 | 22 | Javascript library to parse text into flowcharts, sequence graphs, and more... 23 | 24 | https://github.com/knsv/mermaid 25 | https://github.com/knsv/mermaid 26 | 27 | Comments]]> http://www.echojs.com/news/33778 28 | 29 | How I Setup My VIM as Modern Text Editor 30 | 31 | https://medium.com/@zafarsaleem/how-i-setup-my-vim-as-modern-text-editor-41a93ca4c7a8 32 | https://medium.com/@zafarsaleem/how-i-setup-my-vim-as-modern-text-editor-41a93ca4c7a8 33 | 34 | Comments]]> http://www.echojs.com/news/33777 35 | 36 | Endb - A simple database with multiple dialects. 37 | 38 | https://github.com/chroventer/endb 39 | https://github.com/chroventer/endb 40 | 41 | Comments]]> http://www.echojs.com/news/33774 42 | 43 | Run a NodeJS HTTP ExpressJS Server on Multiple CPU Cores 44 | 45 | https://coderrocketfuel.com/article/run-a-node-js-http-express-js-server-on-multiple-cpu-cores 46 | https://coderrocketfuel.com/article/run-a-node-js-http-express-js-server-on-multiple-cpu-cores 47 | 48 | Comments]]> http://www.echojs.com/news/33773 49 | 50 | Some pretty js clock code collection 51 | 52 | https://a-jie.github.io/clock-shop/ 53 | https://a-jie.github.io/clock-shop/ 54 | 55 | Comments]]> http://www.echojs.com/news/33770 56 | 57 | Painterro - Open Source JS painting library 58 | 59 | https://github.com/ivictbor/painterro/ 60 | https://github.com/ivictbor/painterro/ 61 | 62 | Comments]]> http://www.echojs.com/news/33769 63 | 64 | New ES2019 JavaScript Features Every Developer Should Be Excited About 65 | 66 | https://blog.logrocket.com/new-es2019-javascript-features-every-developer-should-be-excited-about/ 67 | https://blog.logrocket.com/new-es2019-javascript-features-every-developer-should-be-excited-about/ 68 | 69 | Comments]]> http://www.echojs.com/news/33768 70 | 71 | Using Next.js to Build SEO-Friendly React SPAs [Live Demo] 72 | 73 | https://snipcart.com/blog/react-seo-nextjs-tutorial 74 | https://snipcart.com/blog/react-seo-nextjs-tutorial 75 | 76 | Comments]]> http://www.echojs.com/news/33767 77 | 78 | Lighthouse CI Action: audit URLs using Lighthouse and test performance budget 79 | 80 | https://github.com/treosh/lighthouse-ci-action 81 | https://github.com/treosh/lighthouse-ci-action 82 | 83 | Comments]]> http://www.echojs.com/news/33766 84 | 85 | Generate complete api client from express route map using axios 86 | 87 | https://www.npmjs.com/package/axios-api-client-gen 88 | https://www.npmjs.com/package/axios-api-client-gen 89 | 90 | Comments]]> http://www.echojs.com/news/33762 91 | 92 | Surviving The Technical Interview 93 | 94 | https://www.matthewgerstman.com/tech/surviving-the-technical-interview/ 95 | https://www.matthewgerstman.com/tech/surviving-the-technical-interview/ 96 | 97 | Comments]]> http://www.echojs.com/news/33761 98 | 99 | react-ios-pwa-prompt - Native style prompts for Add to Homescreen on iOS 100 | 101 | https://github.com/chrisdancee/react-ios-pwa-prompt 102 | https://github.com/chrisdancee/react-ios-pwa-prompt 103 | 104 | Comments]]> http://www.echojs.com/news/33759 105 | 106 | React Router v5.1 107 | 108 | https://reacttraining.com/blog/react-router-v5-1/ 109 | https://reacttraining.com/blog/react-router-v5-1/ 110 | 111 | Comments]]> http://www.echojs.com/news/33758 112 | 113 | Writing Scalable Architecture For Nodejs 114 | 115 | https://medium.com/free-code-camp/writing-scalable-architecture-for-node-js-2b58e0523d7f 116 | https://medium.com/free-code-camp/writing-scalable-architecture-for-node-js-2b58e0523d7f 117 | 118 | Comments]]> http://www.echojs.com/news/33757 119 | 120 | Methods for defining functions in JavaScript 121 | 122 | https://blog.logrocket.com/defining-functions-in-javascript/ 123 | https://blog.logrocket.com/defining-functions-in-javascript/ 124 | 125 | Comments]]> http://www.echojs.com/news/33755 126 | 127 | Recursion in React: Render Comments with Nested Children 128 | 129 | https://coderrocketfuel.com/article/recursion-in-react-render-comments-with-nested-children 130 | https://coderrocketfuel.com/article/recursion-in-react-render-comments-with-nested-children 131 | 132 | Comments]]> http://www.echojs.com/news/33753 133 | 134 | The Weird World of Infinity in JavaScript 135 | 136 | https://www.impressivewebs.com/infinity-in-javascript/ 137 | https://www.impressivewebs.com/infinity-in-javascript/ 138 | 139 | Comments]]> http://www.echojs.com/news/33752 140 | 141 | How to Build a Slack App to Keep a Slack Channel Topic Locked with Airtable and Standard Library 142 | 143 | https://medium.com/@brimm_reaper/how-to-build-a-slack-app-to-keep-a-slack-channel-topic-locked-with-airtable-and-standard-library-dddea4b9ca03 144 | https://medium.com/@brimm_reaper/how-to-build-a-slack-app-to-keep-a-slack-channel-topic-locked-with-airtable-and-standard-library-dddea4b9ca03 145 | 146 | Comments]]> http://www.echojs.com/news/33751 147 | 148 | The most convenient tool to mock requests for axios, with built-in Chrome extension support. 149 | 150 | https://github.com/eshengsky/axios-mocker 151 | https://github.com/eshengsky/axios-mocker 152 | 153 | Comments]]> http://www.echojs.com/news/33750 154 | 155 | Add File Upload to Your GraphQL API 156 | 157 | https://levelup.gitconnected.com/how-to-add-file-upload-to-your-graphql-api-34d51e341f38?source=friends_link&sk=e112394b7dc3804194e5b0f6656b4bff 158 | https://levelup.gitconnected.com/how-to-add-file-upload-to-your-graphql-api-34d51e341f38?source=friends_link&sk=e112394b7dc3804194e5b0f6656b4bff 159 | 160 | Comments]]> http://www.echojs.com/news/33749 161 | 162 | Vasilisk — Input validation Node.js package 163 | 164 | https://github.com/HarveyDanger/VasiliskJS 165 | https://github.com/HarveyDanger/VasiliskJS 166 | 167 | Comments]]> http://www.echojs.com/news/33748 168 | 169 | How to use event-driven programming in Node.js 170 | 171 | https://blog.logrocket.com/how-to-use-event-driven-programming-in-node-js/ 172 | https://blog.logrocket.com/how-to-use-event-driven-programming-in-node-js/ 173 | 174 | Comments]]> http://www.echojs.com/news/33747 175 | 176 | 3 Mistakes You’re Probably Making When Unit Testing 177 | 178 | https://blog.bitsrc.io/3-mistakes-youre-probably-making-when-unit-testing-d9bdfaf79366 179 | https://blog.bitsrc.io/3-mistakes-youre-probably-making-when-unit-testing-d9bdfaf79366 180 | 181 | Comments]]> http://www.echojs.com/news/33746 182 | 183 | Optimize Cube.js Performance with Pre-Aggregations 184 | 185 | https://cube.dev/blog/high-performance-data-analytics-with-cubejs-pre-aggregations/ 186 | https://cube.dev/blog/high-performance-data-analytics-with-cubejs-pre-aggregations/ 187 | 188 | Comments]]> http://www.echojs.com/news/33745 189 | 190 | Why write CSS in JS 191 | 192 | https://bytex.net/blog/why-write-css-in-js/ 193 | https://bytex.net/blog/why-write-css-in-js/ 194 | 195 | Comments]]> http://www.echojs.com/news/33743 196 | 197 | How to Solve Render Props Callback Hell 198 | 199 | https://dmitripavlutin.com/solve-react-render-props-callback-hell/ 200 | https://dmitripavlutin.com/solve-react-render-props-callback-hell/ 201 | 202 | Comments]]> http://www.echojs.com/news/33742 203 | 204 | REAVIZ 4.0 is out -> Migration from react-pose to @framer motion! The feature list is pretty crazy now - this move gives us SSR! 205 | 206 | https://twitter.com/amcdnl/status/1176236696591110144?s=21 207 | https://twitter.com/amcdnl/status/1176236696591110144?s=21 208 | 209 | Comments]]> http://www.echojs.com/news/33740 210 | 211 | ZooApp 212 | 213 | https://github.com/Mindinventory/ReactZooApp 214 | https://github.com/Mindinventory/ReactZooApp 215 | 216 | Comments]]> http://www.echojs.com/news/33739 217 | -------------------------------------------------------------------------------- /test/fixtures/contentful.rss.xml: -------------------------------------------------------------------------------- 1 | <![CDATA[Contentful - Blog]]>https://www.contentful.comGatsbyJSFri, 27 Sep 2019 16:11:25 GMT<![CDATA[Why I’m going to the Ada Lovelace Festival (and you should, too!) ]]>https://www.contentful.com/blog/2019/09/25/you-should-go-to-ada-lovelace/https://www.contentful.com/blog/2019/09/25/you-should-go-to-ada-lovelace/Wed, 25 Sep 2019 14:00:00 GMT<![CDATA[Keeping it cool: How one engineer uses Contentful to control a fridge in Argentina]]>https://www.contentful.com/blog/2019/09/19/contentful-controls-fridge-argentina/https://www.contentful.com/blog/2019/09/19/contentful-controls-fridge-argentina/Thu, 19 Sep 2019 10:00:00 GMT<![CDATA[Experimentation is now baked into Contentful with new Optimizely app]]>https://www.contentful.com/blog/2019/09/12/experimentation-in-contentful-new-optimizely-app/https://www.contentful.com/blog/2019/09/12/experimentation-in-contentful-new-optimizely-app/Thu, 12 Sep 2019 13:00:00 GMT<![CDATA[Turn your sidebar into a sensitive language check, a preview tool, and more with sidebar extensions ]]>https://www.contentful.com/blog/2019/09/11/sidebar-extensions-release-post/https://www.contentful.com/blog/2019/09/11/sidebar-extensions-release-post/Wed, 11 Sep 2019 12:00:00 GMT<![CDATA[Customize your whole editorial experience with entry extensions]]>https://www.contentful.com/blog/2019/09/09/customize-editorial-experience-entry-extensions/https://www.contentful.com/blog/2019/09/09/customize-editorial-experience-entry-extensions/Mon, 09 Sep 2019 13:45:00 GMT<![CDATA[How dialog extensions make third-party integrations even easier to use]]>https://www.contentful.com/blog/2019/09/03/dialog-extensions/https://www.contentful.com/blog/2019/09/03/dialog-extensions/Tue, 03 Sep 2019 14:45:00 GMT<![CDATA[One night in Brandenburg: A newbie’s-eye-view on the 2019 Contentful offsite]]>https://www.contentful.com/blog/2019/08/30/one-night-in-brandenburg-contentful-offsite-2019/https://www.contentful.com/blog/2019/08/30/one-night-in-brandenburg-contentful-offsite-2019/Fri, 30 Aug 2019 14:00:00 GMT<![CDATA[AMP and Contentful: A flawless customer experience with Jung von Matt/TECH ]]>https://www.contentful.com/blog/2019/08/29/amp-and-contentful-jung-von-matt/https://www.contentful.com/blog/2019/08/29/amp-and-contentful-jung-von-matt/Thu, 29 Aug 2019 11:00:00 GMT<![CDATA[How SEOmonitor aced their app launch (with a little help from Bejamas and Contentful)]]>https://www.contentful.com/blog/2019/08/22/how-seomonitor-aced-their-app-launch/https://www.contentful.com/blog/2019/08/22/how-seomonitor-aced-their-app-launch/Thu, 22 Aug 2019 09:00:00 GMT<![CDATA[Countdown: The top 5 Contentful community talks of all time]]>https://www.contentful.com/blog/2019/08/20/top-5-contentful-community-talks-all-time//https://www.contentful.com/blog/2019/08/20/top-5-contentful-community-talks-all-time//Tue, 20 Aug 2019 09:30:00 GMT<![CDATA[Protecting your Contentful accounts with Have I Been Pwned? ]]>https://www.contentful.com/blog/2019/08/19/protecting-your-contentful-accounts-with-have-i-been-pwned/https://www.contentful.com/blog/2019/08/19/protecting-your-contentful-accounts-with-have-i-been-pwned/Mon, 19 Aug 2019 11:00:00 GMT<![CDATA[Evangelizing design on engineering-heavy product teams ]]>https://www.contentful.com/blog/2019/08/13/evangelizing-design-on-engineering-heavy-product-teams//https://www.contentful.com/blog/2019/08/13/evangelizing-design-on-engineering-heavy-product-teams//Tue, 13 Aug 2019 14:00:00 GMT<![CDATA[Bang & Olufsen increased their e-commerce conversion rate by 60%. Here’s how.]]>https://www.contentful.com/blog/2019/08/12/bang-and-olufsen-case-study/https://www.contentful.com/blog/2019/08/12/bang-and-olufsen-case-study/Mon, 12 Aug 2019 08:00:00 GMT<![CDATA[Everybody’s arguing about headless. Here’s what we know.]]>https://www.contentful.com/blog/2019/08/07/four-debates-about-the-headless-cms/https://www.contentful.com/blog/2019/08/07/four-debates-about-the-headless-cms/Wed, 07 Aug 2019 08:00:00 GMT<![CDATA[Build content-rich progressive web apps with Gatsby and Contentful ]]>https://www.contentful.com/blog/2019/08/05/build-content-rich-progressive-web-apps-with-gatsby-and-contentful/https://www.contentful.com/blog/2019/08/05/build-content-rich-progressive-web-apps-with-gatsby-and-contentful/Mon, 05 Aug 2019 14:00:00 GMT<![CDATA[Fly lighter with static sites: a packing list]]>https://www.contentful.com/blog/2019/07/26/fly-lighter-with-static-sites-a-packing-list/https://www.contentful.com/blog/2019/07/26/fly-lighter-with-static-sites-a-packing-list/Fri, 26 Jul 2019 08:00:00 GMT<![CDATA[What everyone needs to know about localization with Locaria]]>https://www.contentful.com/blog/2019/07/25/localization-with-locaria/https://www.contentful.com/blog/2019/07/25/localization-with-locaria/Thu, 25 Jul 2019 12:00:00 GMT<![CDATA[The delivery team vs. the two-pizza team]]>https://www.contentful.com/blog/2019/07/23/delivery-team-vs-two-pizza-team/https://www.contentful.com/blog/2019/07/23/delivery-team-vs-two-pizza-team/Mon, 22 Jul 2019 22:00:00 GMT<![CDATA[The modern content paradigm with TribalScale ]]>https://www.contentful.com/blog/2019/07/10/the-modern-content-paradigm-with-tribalscale/https://www.contentful.com/blog/2019/07/10/the-modern-content-paradigm-with-tribalscale/Wed, 10 Jul 2019 14:00:00 GMT<![CDATA[Editors, rejoice! Contentful is built for you ]]>https://www.contentful.com/blog/2019/07/03/editorial-roundup/https://www.contentful.com/blog/2019/07/03/editorial-roundup/Wed, 03 Jul 2019 16:00:00 GMT<![CDATA[A new way to organise your teams in Contentful ]]>https://www.contentful.com/blog/2019/07/02/introducing-teams/https://www.contentful.com/blog/2019/07/02/introducing-teams/Tue, 02 Jul 2019 08:00:00 GMT<![CDATA[We’re ISO 27001-compliant. Here’s why that’s important ]]>https://www.contentful.com/blog/2019/06/24/iso-certification-announcement/https://www.contentful.com/blog/2019/06/24/iso-certification-announcement/Mon, 24 Jun 2019 08:00:00 GMT<![CDATA[Everything you need to know about GraphQL]]>https://www.contentful.com/blog/2019/06/20/everything-you-wanted-to-know-about-graphql/https://www.contentful.com/blog/2019/06/20/everything-you-wanted-to-know-about-graphql/Wed, 19 Jun 2019 22:00:00 GMT<![CDATA[The retail disconnect: Content is currency in retail, but retailers are wasting it]]>https://www.contentful.com/blog/2019/06/18/ecommerce-digital-transformation/https://www.contentful.com/blog/2019/06/18/ecommerce-digital-transformation/Tue, 18 Jun 2019 12:00:00 GMT<![CDATA[Getting started with Contentful UI Extensions: Part 1]]>https://www.contentful.com/blog/2019/06/17/getting-started-with-contentful-ui-extensions-one/https://www.contentful.com/blog/2019/06/17/getting-started-with-contentful-ui-extensions-one/Sun, 16 Jun 2019 22:00:00 GMT<![CDATA[Build a website using Nuxt and Contentful : a step by step guide]]>https://www.contentful.com/blog/2019/06/14/build-website-nuxt-contentful/https://www.contentful.com/blog/2019/06/14/build-website-nuxt-contentful/Fri, 14 Jun 2019 14:00:00 GMT<![CDATA[Tech giants are investing in digital transformation: you should too ]]>https://www.contentful.com/blog/2019/06/13/tech-giants-are-investing-in-digital-transformation/https://www.contentful.com/blog/2019/06/13/tech-giants-are-investing-in-digital-transformation/Thu, 13 Jun 2019 08:30:00 GMT<![CDATA[Editors, here's how translation works with Contentful (spoiler: it's a breeze) ]]>https://www.contentful.com/blog/2019/06/10/editors-and-translation/https://www.contentful.com/blog/2019/06/10/editors-and-translation/Mon, 10 Jun 2019 10:00:00 GMT<![CDATA[Wave hello to our new brand (and goodbye to your CMS problems, too)]]>https://www.contentful.com/blog/2019/06/06/introducing-a-new-look/https://www.contentful.com/blog/2019/06/06/introducing-a-new-look/Thu, 06 Jun 2019 10:00:00 GMT<![CDATA[Get traction with interaction: modeling interactive content with React and Contentful's rich text editor]]>https://www.contentful.com/blog/2019/05/31/interactive-content-react-rich-text-editor/https://www.contentful.com/blog/2019/05/31/interactive-content-react-rich-text-editor/Fri, 31 May 2019 11:00:00 GMT<![CDATA[How a support engineer makes your life easier]]>https://www.contentful.com/blog/2019/05/24/how-a-support-engineer-makes-your-life-easier/https://www.contentful.com/blog/2019/05/24/how-a-support-engineer-makes-your-life-easier/Fri, 24 May 2019 16:00:00 GMT<![CDATA[Building a better podcast with Contentful and Sinatra]]>https://www.contentful.com/blog/2019/05/22/building-a-better-podcast-with-contentful-and-sinatra/https://www.contentful.com/blog/2019/05/22/building-a-better-podcast-with-contentful-and-sinatra/Wed, 22 May 2019 11:00:00 GMT<![CDATA[Configuring automatic backups for Contentful]]>https://www.contentful.com/blog/2019/05/15/configuring-automatic-backups-for-contentful/https://www.contentful.com/blog/2019/05/15/configuring-automatic-backups-for-contentful/Wed, 15 May 2019 15:00:00 GMT<![CDATA[Ramp up your productivity and host a hackathon. Here's how to do it. ]]>https://www.contentful.com/blog/2019/05/08/how-to-host-a-hackathon/https://www.contentful.com/blog/2019/05/08/how-to-host-a-hackathon/Wed, 08 May 2019 15:00:00 GMT<![CDATA[3 ways to optimize your global localization strategy ]]>https://www.contentful.com/blog/2019/05/01/3-ways-to-optimize-your-global-localization-strategy/https://www.contentful.com/blog/2019/05/01/3-ways-to-optimize-your-global-localization-strategy/Wed, 01 May 2019 16:00:00 GMT<![CDATA[What we shipped: 5 new business features which are (basically) a love letter to you ]]>https://www.contentful.com/blog/2019/04/30/what-we-shipped-april/https://www.contentful.com/blog/2019/04/30/what-we-shipped-april/Tue, 30 Apr 2019 06:00:00 GMT<![CDATA[Tagging and finding images made easy with AI]]>https://www.contentful.com/blog/2019/04/29/tagging-and-finding-images-made-easy-with-ai/https://www.contentful.com/blog/2019/04/29/tagging-and-finding-images-made-easy-with-ai/Mon, 29 Apr 2019 11:00:00 GMT<![CDATA[Content Preview for your Contentful-Gatsby site with Next.js]]>https://www.contentful.com/blog/2019/04/24/content-preview-for-your-contentful-gatsby-site-with-nextjs/https://www.contentful.com/blog/2019/04/24/content-preview-for-your-contentful-gatsby-site-with-nextjs/Wed, 24 Apr 2019 13:00:00 GMT<![CDATA[How to organize your content and untangle your site’s navigation ]]>https://www.contentful.com/blog/2019/04/19/organise-your-content-whitepaper/https://www.contentful.com/blog/2019/04/19/organise-your-content-whitepaper/Fri, 19 Apr 2019 16:00:00 GMT<![CDATA[Preview: Getting editors and developers on the same page ]]>https://www.contentful.com/blog/2019/04/17/preview-editors-and-developers/https://www.contentful.com/blog/2019/04/17/preview-editors-and-developers/Wed, 17 Apr 2019 11:00:00 GMT<![CDATA[Help! My terminal speaks Contentful ]]>https://www.contentful.com/blog/2019/04/12/help-my-terminal-speaks-contentful/https://www.contentful.com/blog/2019/04/12/help-my-terminal-speaks-contentful/Fri, 12 Apr 2019 16:00:00 GMT<![CDATA[Girls’ Day 2019: welcoming the next generation of women to Contentful]]>https://www.contentful.com/blog/2019/04/09/girls-day-2019/https://www.contentful.com/blog/2019/04/09/girls-day-2019/Tue, 09 Apr 2019 16:00:00 GMT<![CDATA[How to create a blog using Nuxt.js and Contentful]]>https://www.contentful.com/blog/2019/04/05/nuxtjs-contentful-seo-blog/https://www.contentful.com/blog/2019/04/05/nuxtjs-contentful-seo-blog/Fri, 05 Apr 2019 12:00:00 GMT<![CDATA[Best practices for optimizing your digital editorial workflow]]>https://www.contentful.com/blog/2019/04/04/optimize-your-digital-workflow/https://www.contentful.com/blog/2019/04/04/optimize-your-digital-workflow/Thu, 04 Apr 2019 16:00:00 GMT<![CDATA[Contentful is more secure than ever: our path to ISO certification]]>https://www.contentful.com/blog/2019/03/29/contentful-iso-certification-better-security/https://www.contentful.com/blog/2019/03/29/contentful-iso-certification-better-security/Fri, 29 Mar 2019 13:30:00 GMT<![CDATA[Why your content management system is driving away top talent]]>https://www.contentful.com/blog/2019/03/27/your-cms-drives-away-top-talent/https://www.contentful.com/blog/2019/03/27/your-cms-drives-away-top-talent/Wed, 27 Mar 2019 12:00:00 GMT<![CDATA[Building a modern marketing stack at Optimizely: content management ]]>https://www.contentful.com/blog/2019/03/20/building-a-modern-marketing-stack-with-contentful-optimizely/https://www.contentful.com/blog/2019/03/20/building-a-modern-marketing-stack-with-contentful-optimizely/Wed, 20 Mar 2019 15:00:00 GMT<![CDATA[Unlock a better way to work with Contentful’s content infrastructure]]>https://www.contentful.com/blog/2019/03/18/better-way-to-work-with-content-infrastructure/https://www.contentful.com/blog/2019/03/18/better-way-to-work-with-content-infrastructure/Mon, 18 Mar 2019 15:45:00 GMT<![CDATA[Every marketing team needs content operations. Here's why. ]]>https://www.contentful.com/blog/2019/03/15/marketing-team-needs-content-operations/https://www.contentful.com/blog/2019/03/15/marketing-team-needs-content-operations/Fri, 15 Mar 2019 15:00:00 GMT<![CDATA[Implementing GraphQL with a REST API]]>https://www.contentful.com/blog/2019/03/13/implementing-graphql-rest-api/https://www.contentful.com/blog/2019/03/13/implementing-graphql-rest-api/Wed, 13 Mar 2019 07:00:00 GMT<![CDATA[Authoring hub: a modern way of unifying content operations]]>https://www.contentful.com/blog/2019/03/12/authoring-hub-feature-release/https://www.contentful.com/blog/2019/03/12/authoring-hub-feature-release/Tue, 12 Mar 2019 10:00:00 GMT<![CDATA[Who really manages your content and why it matters ]]>https://www.contentful.com/blog/2019/03/07/who-really-manages-your-content/https://www.contentful.com/blog/2019/03/07/who-really-manages-your-content/Thu, 07 Mar 2019 17:00:00 GMT<![CDATA[4 things we learned when migrating content to Contentful ]]>https://www.contentful.com/blog/2019/03/07/lessons-in-migrating-to-contentful/https://www.contentful.com/blog/2019/03/07/lessons-in-migrating-to-contentful/Thu, 07 Mar 2019 16:30:00 GMT<![CDATA[10 principles to build interfaces you won’t hate]]>https://www.contentful.com/blog/2019/03/05/10-principles-building-interfaces-design/https://www.contentful.com/blog/2019/03/05/10-principles-building-interfaces-design/Tue, 05 Mar 2019 17:30:00 GMT<![CDATA[Marketing change log: What used to take weeks now takes minutes]]>https://www.contentful.com/blog/2019/02/28/marketing-change-log/https://www.contentful.com/blog/2019/02/28/marketing-change-log/Wed, 27 Feb 2019 23:00:00 GMT<![CDATA[From WordPress to static site—the Contentful way]]>https://www.contentful.com/blog/2019/02/27/wordpress-move-contentful-static-site/https://www.contentful.com/blog/2019/02/27/wordpress-move-contentful-static-site/Wed, 27 Feb 2019 15:00:00 GMT<![CDATA[How to build a React Redux application with API-first CMS Contentful]]>https://www.contentful.com/blog/2019/02/18/how-build-react-redux-app/https://www.contentful.com/blog/2019/02/18/how-build-react-redux-app/Mon, 18 Feb 2019 17:00:00 GMT<![CDATA[Contentful February developer update]]>https://www.contentful.com/blog/2019/02/18/contentful-feb-2019-developer-update/https://www.contentful.com/blog/2019/02/18/contentful-feb-2019-developer-update/Mon, 18 Feb 2019 16:00:00 GMT<![CDATA[What’s in it for me? 6 of our favourite features for editors using Contentful ]]>https://www.contentful.com/blog/2019/02/15/favourite-features-for-editors/https://www.contentful.com/blog/2019/02/15/favourite-features-for-editors/Fri, 15 Feb 2019 16:00:00 GMT<![CDATA[Keeping data secure and security high with mobile device management]]>https://www.contentful.com/blog/2019/02/07/data-secure-security-mobile-device-management/https://www.contentful.com/blog/2019/02/07/data-secure-security-mobile-device-management/Thu, 07 Feb 2019 11:00:00 GMT<![CDATA[Master Contentful: 8 tips for editors ]]>https://www.contentful.com/blog/2019/02/06/master-contentful-eight-tips-for-editors/https://www.contentful.com/blog/2019/02/06/master-contentful-eight-tips-for-editors/Wed, 06 Feb 2019 14:00:00 GMT<![CDATA[Developing an Apple Music search into Contentful using UI extensions]]>https://www.contentful.com/blog/2019/02/05/develop-apple-music-search-contentful-ui-extensions/https://www.contentful.com/blog/2019/02/05/develop-apple-music-search-contentful-ui-extensions/Tue, 05 Feb 2019 10:00:00 GMT<![CDATA[Headless, decoupled and Contentful: A non-technical explanation for the confused ]]>https://www.contentful.com/blog/2019/02/04/difference-between-headless-decoupled-contentful/https://www.contentful.com/blog/2019/02/04/difference-between-headless-decoupled-contentful/Mon, 04 Feb 2019 17:00:00 GMT<![CDATA[Launching Eviction Free NYC using GatsbyJS, GraphQL and Contentful]]>https://www.contentful.com/blog/2019/01/31/launch-eviction-free-nyc-gatsbyjs-graphql-contentful/https://www.contentful.com/blog/2019/01/31/launch-eviction-free-nyc-gatsbyjs-graphql-contentful/Thu, 31 Jan 2019 19:00:00 GMT<![CDATA[Combining APIs using GraphQL schema stitching: Part 2]]>https://www.contentful.com/blog/2019/01/30/combining-apis-graphql-schema-stitching-part-2/https://www.contentful.com/blog/2019/01/30/combining-apis-graphql-schema-stitching-part-2/Wed, 30 Jan 2019 16:00:00 GMT<![CDATA[Contentful January developer update]]>https://www.contentful.com/blog/2019/01/29/contentful-dec-2019-developer-update/https://www.contentful.com/blog/2019/01/29/contentful-dec-2019-developer-update/Tue, 29 Jan 2019 15:00:00 GMT<![CDATA[GraphQL and optimizing for developer experience]]>https://www.contentful.com/blog/2019/01/25/graphql-optimize-developer-experience/https://www.contentful.com/blog/2019/01/25/graphql-optimize-developer-experience/Fri, 25 Jan 2019 15:00:00 GMT<![CDATA[Creating a content calendar for your Contentful content]]>https://www.contentful.com/blog/2019/01/24/create-content-calendar-contentful/https://www.contentful.com/blog/2019/01/24/create-content-calendar-contentful/Thu, 24 Jan 2019 16:00:00 GMT<![CDATA[Combining APIs using GraphQL schema stitching: Part 1]]>https://www.contentful.com/blog/2019/01/23/combining-apis-graphql-schema-stitching-part-1/https://www.contentful.com/blog/2019/01/23/combining-apis-graphql-schema-stitching-part-1/Wed, 23 Jan 2019 15:00:00 GMT<![CDATA[Year in review 2018: Contentful blog]]>https://www.contentful.com/blog/2019/01/23/year-in-review-2018/https://www.contentful.com/blog/2019/01/23/year-in-review-2018/Wed, 23 Jan 2019 09:00:00 GMT<![CDATA[Building Flexible Content Models — On a Budget]]>https://www.contentful.com/blog/2019/01/11/building-flexible-content-models-budget/https://www.contentful.com/blog/2019/01/11/building-flexible-content-models-budget/Fri, 11 Jan 2019 16:00:00 GMT<![CDATA[How GraphQL is paving the way for agile organizations ]]>https://www.contentful.com/blog/2019/01/04/business-use-case-graphql-agile-organizations/https://www.contentful.com/blog/2019/01/04/business-use-case-graphql-agile-organizations/Fri, 04 Jan 2019 16:30:00 GMT<![CDATA[The big role of Domain Driven Architecture and GraphQL in digital transformation]]>https://www.contentful.com/blog/2018/12/27/role-domain-driven-architecture-graphql-digital-transformation/https://www.contentful.com/blog/2018/12/27/role-domain-driven-architecture-graphql-digital-transformation/Thu, 27 Dec 2018 07:00:00 GMT<![CDATA[Contentful December developer update]]>https://www.contentful.com/blog/2018/12/24/contentful-dec-2018-developer-update/https://www.contentful.com/blog/2018/12/24/contentful-dec-2018-developer-update/Mon, 24 Dec 2018 17:00:00 GMT<![CDATA[Dynamic schema generation for changing data models]]>https://www.contentful.com/blog/2018/12/21/dynamic-schema-generation-changing-data-models/https://www.contentful.com/blog/2018/12/21/dynamic-schema-generation-changing-data-models/Fri, 21 Dec 2018 14:00:00 GMT<![CDATA[Doubling down on security: passwords and best practices]]>https://www.contentful.com/blog/2018/12/20/security-passwords-best-practices/https://www.contentful.com/blog/2018/12/20/security-passwords-best-practices/Thu, 20 Dec 2018 10:00:00 GMT<![CDATA[Personalizing the web for the modern user with Contentful and Frosmo]]>https://www.contentful.com/blog/2018/12/13/frosmo-contentful-website-personalization/https://www.contentful.com/blog/2018/12/13/frosmo-contentful-website-personalization/Thu, 13 Dec 2018 09:00:00 GMT<![CDATA[ISO 27001 and PCI DSS Certification - What this means for keeping you safe]]>https://www.contentful.com/blog/2018/12/07/security-iso-27001-pci-dss-certification/https://www.contentful.com/blog/2018/12/07/security-iso-27001-pci-dss-certification/Fri, 07 Dec 2018 09:00:00 GMT<![CDATA[From web CMS to a new way of building software: A new round of funding validates our market strategy]]>https://www.contentful.com/blog/2018/12/05/funding-series-d/https://www.contentful.com/blog/2018/12/05/funding-series-d/Wed, 05 Dec 2018 14:00:00 GMT<![CDATA[Meet the Example App - your kick-off point for developing with Contentful]]>https://www.contentful.com/blog/2018/12/04/example-app-introduction-development-web-project/https://www.contentful.com/blog/2018/12/04/example-app-introduction-development-web-project/Tue, 04 Dec 2018 12:00:00 GMT<![CDATA[A loosely-coupled approach to creating MailChimp campaigns with Contentful webhooks]]>https://www.contentful.com/blog/2018/11/30/mailchimp-campaign-contentful-webhooks-loose-coupling/https://www.contentful.com/blog/2018/11/30/mailchimp-campaign-contentful-webhooks-loose-coupling/Fri, 30 Nov 2018 08:30:00 GMT<![CDATA[More than just serving JSON – five requirements of your next API-first CMS]]>https://www.contentful.com/blog/2018/11/29/requirements-api-first-cms-json/https://www.contentful.com/blog/2018/11/29/requirements-api-first-cms-json/Thu, 29 Nov 2018 07:00:00 GMT<![CDATA[Writing GraphQL queries in native Ruby = Love ❤️]]>https://www.contentful.com/blog/2018/11/20/graphql-ruby-love-backend-developer/https://www.contentful.com/blog/2018/11/20/graphql-ruby-love-backend-developer/Tue, 20 Nov 2018 07:00:00 GMT<![CDATA[Fall ‘18 Release Train: Increasing Developer Productivity and Connecting to the Modern Stack]]>https://www.contentful.com/blog/2018/11/19/new-features-fall-2018-developer-productivity-modern-stack/https://www.contentful.com/blog/2018/11/19/new-features-fall-2018-developer-productivity-modern-stack/Mon, 19 Nov 2018 19:00:00 GMT<![CDATA[Contentful emerges as a Contender in the 2018 Forrester Wave: Web Content Management Systems]]>https://www.contentful.com/blog/2018/11/15/contentful-emerges-as-a-contender-forrester-wave/https://www.contentful.com/blog/2018/11/15/contentful-emerges-as-a-contender-forrester-wave/Thu, 15 Nov 2018 11:00:00 GMT<![CDATA[Contentful November developer update]]>https://www.contentful.com/blog/2018/11/13/contentful-nov-2018-developer-update/https://www.contentful.com/blog/2018/11/13/contentful-nov-2018-developer-update/Tue, 13 Nov 2018 17:00:00 GMT<![CDATA[How to automatically watermark Contentful assets using a serverless function]]>https://www.contentful.com/blog/2018/11/08/how-automatically-watermark-contentful-assets-using-serverless-function-jimp/https://www.contentful.com/blog/2018/11/08/how-automatically-watermark-contentful-assets-using-serverless-function-jimp/Thu, 08 Nov 2018 14:00:00 GMT<![CDATA[Five workflows loved by digital teams]]>https://www.contentful.com/blog/2018/11/07/five-workflows-loved-by-digital-teams/https://www.contentful.com/blog/2018/11/07/five-workflows-loved-by-digital-teams/Wed, 07 Nov 2018 21:00:00 GMT<![CDATA[How to Build an Ecommerce Static Site with Jekyll, Contentful, and Commerce Layer]]>https://www.contentful.com/blog/2018/11/07/how-build-static-site-ecommerce-jekyll-contentful-commerce-layer/https://www.contentful.com/blog/2018/11/07/how-build-static-site-ecommerce-jekyll-contentful-commerce-layer/Wed, 07 Nov 2018 13:00:00 GMT<![CDATA[Using content infrastructure for better digital experiences]]>https://www.contentful.com/blog/2018/10/25/using-content-infrastructure-for-better-digital-experiences/https://www.contentful.com/blog/2018/10/25/using-content-infrastructure-for-better-digital-experiences/Thu, 25 Oct 2018 10:00:00 GMT<![CDATA[Why a leading broadcaster drop-kicked multiple CMSes in favor of a unified content hub]]>https://www.contentful.com/blog/2018/10/16/content-hub-case-study/https://www.contentful.com/blog/2018/10/16/content-hub-case-study/Tue, 16 Oct 2018 10:00:00 GMT<![CDATA[Contentful responds to market shift with new partner program]]>https://www.contentful.com/blog/2018/10/16/partner-program-launch/https://www.contentful.com/blog/2018/10/16/partner-program-launch/Tue, 16 Oct 2018 10:00:00 GMT<![CDATA[Contentful October developer update]]>https://www.contentful.com/blog/2018/10/12/contentful-oct-2018-developer-update/https://www.contentful.com/blog/2018/10/12/contentful-oct-2018-developer-update/Fri, 12 Oct 2018 10:00:00 GMT<![CDATA[3 Methods to resolve GraphQL endpoints]]>https://www.contentful.com/blog/2018/09/25/3-methods-resolve-graphql-endpoints/https://www.contentful.com/blog/2018/09/25/3-methods-resolve-graphql-endpoints/Tue, 25 Sep 2018 13:00:00 GMT<![CDATA[Contentful September developer update]]>https://www.contentful.com/blog/2018/09/24/contentful-sept-2018-developer-update/https://www.contentful.com/blog/2018/09/24/contentful-sept-2018-developer-update/Mon, 24 Sep 2018 10:00:00 GMT<![CDATA[DFDS goes serverless]]>https://www.contentful.com/blog/2018/09/19/dfds-goes-serverless/https://www.contentful.com/blog/2018/09/19/dfds-goes-serverless/Wed, 19 Sep 2018 12:00:00 GMT<![CDATA[Content model changes at scale – TELUS and CMS as Code]]>https://www.contentful.com/blog/2018/09/13/content-model-changes-scale-telus-cms-as-code/https://www.contentful.com/blog/2018/09/13/content-model-changes-scale-telus-cms-as-code/Thu, 13 Sep 2018 06:00:00 GMT<![CDATA[Contentful joins forces with Optimizely, Atlassian, AWS and more to deliver modern digital experiences faster]]>https://www.contentful.com/blog/2018/09/12/contentful-joins-dxsalliance-delivers-digital-experiences-faster/https://www.contentful.com/blog/2018/09/12/contentful-joins-dxsalliance-delivers-digital-experiences-faster/Wed, 12 Sep 2018 16:30:00 GMT<![CDATA[The modern website strategy guide: how content infrastructure is accelerating digital products]]>https://www.contentful.com/blog/2018/08/21/content-infrastructure-accelerates-digital-products/https://www.contentful.com/blog/2018/08/21/content-infrastructure-accelerates-digital-products/Tue, 21 Aug 2018 09:20:00 GMT<![CDATA[A beginner's guide to creating a static site using React, Gatsby, Contentful and Netlify]]>https://www.contentful.com/blog/2018/08/17/a-guide-to-creating-static-sites-with-react-gatsby-contentful-and-netlify/https://www.contentful.com/blog/2018/08/17/a-guide-to-creating-static-sites-with-react-gatsby-contentful-and-netlify/Fri, 17 Aug 2018 09:00:00 GMT --------------------------------------------------------------------------------
            .*<\/td><\/tr><\/tbody><\/table>/ 258 | ); 259 | 260 | done(); 261 | } 262 | ); 263 | }); 264 | }); 265 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # jquery.rss [![Build Status](https://travis-ci.org/sdepold/jquery-rss.svg?branch=master)](https://travis-ci.org/sdepold/jquery-rss) 2 | 3 | This plugin can be used to read a RSS feed and transform it into a custom piece of HTML. 4 | 5 | ## Alternatives 6 | 7 | A vanilla JavaScript version of this library can be found here: [Vanilla RSS](https://github.com/sdepold/vanilla-rss). 8 | This plugin uses [Feedr](https://github.com/sdepold/feedr), a backend server that parses and converts RSS feeds into its JSON representation. The server was built as a drop-in replacement for Google's former Feed API. 9 | 10 | ## Support 11 | 12 | Since version 3.4.0 of jquery.rss, users have the chance to support funding future developments and 13 | covering the costs for the hosting of jquery.rss' respective server side companion app [feedr](https://github.com/sdepold/feedr). 14 | 15 | Every once in a while supporters will get affiliate links instead of one of the feed's entries. 16 | 17 | If you are not interested in supporting the authors of the plugin, then you can easily opt-out of it by setting the respective 18 | `support` option. See below for further details. 19 | 20 | Thanks in advance! 21 | 22 | [](https://api.gitsponsors.com/api/badge/link?p=A870wDxKfRAOTGj5zfrruTIaAUH5KMuTcc8qZo/ABHcL9bsKxfQ89vfZIu6FlxjMgvjzh1PAZfe22FPRwvNsdXbKvTP4hf0ylU/qVVAK02Pp0/N9TTTG4dzEL8Ih37BUjXI5bBZkUEuR6x9ByLWfLA==) 23 | 24 | ## Installation 25 | 26 | Through npm: 27 | 28 | ``` 29 | $ npm install jquery 30 | $ npm install jquery-rss 31 | 32 | const $ = require('jquery'); 33 | require('jquery-rss'); // This will add the plugin to the jQuery namespace 34 | ``` 35 | 36 | Through cdnjs: 37 | 38 | ``` 39 | 40 | 41 | ``` 42 | 43 | ## Setup 44 | 45 | ```html 46 | 47 | 48 | 49 | jquery.rss example 50 | 51 | 52 | 53 | 58 | 59 | 60 |
            61 | 62 | 63 | ``` 64 | 65 | Demo link for above code: http://embed.plnkr.co/WQRoCYLld162uplnz1rc/preview 66 | 67 | Note: Moment.js is _optional_. If you include it, jquery.rss will use it to format dates. 68 | If you do not want to include Moment.js, you may opt for providing your own date formatting function, or for not formatting dates at all. 69 | 70 | ## Options 71 | 72 | ```javascript 73 | $("#rss-feeds").rss( 74 | // You can either provide a single feed URL or a list of URLs (via an array) 75 | "http://feeds.feedburner.com/premiumpixels", 76 | { 77 | // how many entries do you want? 78 | // default: 4 79 | // valid values: any integer 80 | limit: 10, 81 | 82 | // want to offset results being displayed? 83 | // default: false 84 | // valid values: any integer 85 | offsetStart: false, // offset start point 86 | offsetEnd: false, // offset end point 87 | 88 | // will request the API via https 89 | // default: false 90 | // valid values: false, true 91 | ssl: true, 92 | 93 | // which server should be requested for feed parsing 94 | // the server implementation is here: https://github.com/sdepold/feedr 95 | // default: feedrapp.info 96 | // valid values: any string 97 | host: "my-own-feedr-instance.com", 98 | 99 | // option to seldomly render ads 100 | // ads help covering the costs for the feedrapp server hosting and future improvements 101 | // default: true 102 | // valid values: false, true 103 | support: false, 104 | 105 | // outer template for the html transformation 106 | // default: "
              {entries}
            " 107 | // valid values: any string 108 | layoutTemplate: "
            {entries}
            ", 109 | 110 | // inner template for each entry 111 | // default: '
          • [{author}@{date}] {title}
            {shortBodyPlain}
          • ' 112 | // valid values: any string 113 | entryTemplate: "

            {title}

            ", 114 | 115 | // additional token definition for in-template-usage 116 | // default: {} 117 | // valid values: any object/hash 118 | tokens: { 119 | foo: "bar", 120 | bar: function(entry, tokens) { 121 | return entry.title; 122 | } 123 | }, 124 | 125 | // formats the date with moment.js (optional) 126 | // default: 'dddd MMM Do' 127 | // valid values: see http://momentjs.com/docs/#/displaying/ 128 | dateFormat: "MMMM Do, YYYY", 129 | 130 | // localizes the date with moment.js (optional) 131 | // default: 'en' 132 | dateLocale: "de", 133 | 134 | // Defines the format which is used for the feed. 135 | // Default: null (utf8) 136 | // valid values: https://github.com/ashtuchkin/iconv-lite/wiki/Supported-Encodings 137 | encoding: "ISO-8859-1", 138 | 139 | // Defined the order of the feed's entries. 140 | // Default: undefined (keeps the order of the original feed) 141 | // valid values: All entry properties; title, link, content, contentSnippet, publishedDate, categories, author, thumbnail 142 | // Order can be reversed by prefixing a dash (-) 143 | order: "-publishedDate", 144 | 145 | // formats the date in whatever manner you choose. (optional) 146 | // this function should return your formatted date. 147 | // this is useful if you want to format dates without moment.js. 148 | // if you don't use moment.js and don't define a dateFormatFunction, the dates will 149 | // not be formatted; they will appear exactly as the RSS feed gives them to you. 150 | dateFormatFunction: function(date) {}, 151 | 152 | // a callback, which gets triggered when an error occurs 153 | // default: function() { throw new Error("jQuery RSS: url don't link to RSS-Feed") } 154 | error: function() {}, 155 | 156 | // a callback, which gets triggered when everything was loaded successfully 157 | // this is an alternative to the next parameter (callback function) 158 | // default: function(){} 159 | success: function() {}, 160 | 161 | // a callback, which gets triggered once data was received but before the rendering. 162 | // this can be useful when you need to remove a spinner or something similar 163 | onData: function() {} 164 | }, 165 | 166 | // callback function 167 | // called after feeds are successfully loaded and after animations are done 168 | function callback() {} 169 | ); 170 | ``` 171 | 172 | ### Note about the host option 173 | 174 | Since version 3.0.0 the plugin is no longer using the Google Feed API but a drop-in replacement called [feedr](https://feedrapp.info). That server is currently running on Heroku and might have some downtimes, interruptions or unexpected issues. While I will try to keep those problems as rare as possible, it can totally happen from time to time. I might move the service to some other provide or even improve the infrastructure. 175 | 176 | If you don't want to rely on the [provided server](http://feedrapp.info) and instead run your own version, you can just download feedr, install the dependencies and run it. As written above, you can specify the host which is used to parse the feeds with the `host` option. 177 | 178 | ## Templating 179 | 180 | As seen in the options, you can specify a template in order to transform the json objects into HTML. In order to that, you can either define the outer template (which describes the html around the entries) or the entry template (which describes the html of an entry). 181 | 182 | The basic format of those templates are: 183 | 184 | ```html 185 | 186 | "{entries}" 187 | 188 | 189 | "{token1}{token2}" 190 | ``` 191 | 192 | So, let's say you have specified a limit of 2, using the upper pseudo html. This will result in the following: 193 | 194 | ```html 195 | 196 | {token1}{token2} 197 | {token1}{token2} 198 | 199 | ``` 200 | 201 | There are some predefined tokens: 202 | 203 | - url: the url to the post 204 | - author: the author of the post 205 | - date: the publishing date 206 | - title: the title of the post 207 | - body: the complete content of the post 208 | - shortBody: the shortened content of the post 209 | - bodyPlain: the complete content of the post without html 210 | - shortBodyPlain: the shortened content of the post without html 211 | - teaserImage: the first image in the post's body 212 | - teaserImageUrl: the url of the first image in the post's body 213 | - index: the index of the current entry 214 | - totalEntries: the total count of the entries 215 | - feed: contains high level information of the feed (e.g. title of the website) 216 | 217 | You can also define custom tokens using the `tokens` option: 218 | 219 | ```javascript 220 | $("#foo").rss(url, { 221 | entryTemplate: "{dynamic}, {static}, {re-use}", 222 | tokens: { 223 | dynamic: function(entry, tokens) { 224 | return "dynamic-stuff: " + entry.title; 225 | }, 226 | "re-use": function(entry, tokens) { 227 | return encodeURIComponent(tokens.teaserImageUrl); 228 | }, 229 | static: "static" 230 | } 231 | }); 232 | ``` 233 | 234 | Please make sure to NOT define infinite loops. The following example is really BAD: 235 | 236 | ```javascript 237 | $('#foo').rss(url, { 238 | entryTemplate: "{loop}", 239 | tokens: { 240 | whoops: function(entry, tokens) { return tokens.loop() } 241 | loop: function(entry, tokens) { return tokens.whoops() } 242 | } 243 | }) 244 | ``` 245 | 246 | Here is a real-world example: 247 | 248 | ```javascript 249 | $("#foo").rss(url, { 250 | layoutTemplate: "{entries}
            Title
            ", 251 | entryTemplate: "
            {title}