Blame static/assets/js/jquery.rss.js

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