Revision 97b7ae4c

/dev/null
1
/*!
2
 * JavaScript Linkify - v0.3 - 6/27/2009
3
 * http://benalman.com/projects/javascript-linkify/
4
 * 
5
 * Copyright (c) 2009 "Cowboy" Ben Alman
6
 * Dual licensed under the MIT and GPL licenses.
7
 * http://benalman.com/about/license/
8
 * 
9
 * Some regexps adapted from http://userscripts.org/scripts/review/7122
10
 */
11

  
12
// Script: JavaScript Linkify: Process links in text!
13
//
14
// *Version: 0.3, Last updated: 6/27/2009*
15
// 
16
// Project Home - http://benalman.com/projects/javascript-linkify/
17
// GitHub       - http://github.com/cowboy/javascript-linkify/
18
// Source       - http://github.com/cowboy/javascript-linkify/raw/master/ba-linkify.js
19
// (Minified)   - http://github.com/cowboy/javascript-linkify/raw/master/ba-linkify.min.js (2.8kb)
20
// 
21
// About: License
22
// 
23
// Copyright (c) 2009 "Cowboy" Ben Alman,
24
// Dual licensed under the MIT and GPL licenses.
25
// http://benalman.com/about/license/
26
// 
27
// About: Examples
28
// 
29
// This working example, complete with fully commented code, illustrates one way
30
// in which this code can be used.
31
// 
32
// Linkify - http://benalman.com/code/projects/javascript-linkify/examples/linkify/
33
// 
34
// About: Support and Testing
35
// 
36
// Information about what browsers this code has been tested in.
37
// 
38
// Browsers Tested - Internet Explorer 6-8, Firefox 2-3.7, Safari 3-4, Chrome, Opera 9.6-10.
39
// 
40
// About: Release History
41
// 
42
// 0.3 - (6/27/2009) Initial release
43

  
44
// Function: linkify
45
// 
46
// Turn text into linkified html.
47
// 
48
// Usage:
49
// 
50
//  > var html = linkify( text [, options ] );
51
// 
52
// Arguments:
53
// 
54
//  text - (String) Non-HTML text containing links to be parsed.
55
//  options - (Object) An optional object containing linkify parse options.
56
// 
57
// Options:
58
// 
59
//  callback (Function) - If specified, this will be called once for each link-
60
//    or non-link-chunk with two arguments, text and href. If the chunk is
61
//    non-link, href will be omitted. If unspecified, the default linkification
62
//    callback is used.
63
//  punct_regexp (RegExp) - A RegExp that will be used to trim trailing
64
//    punctuation from links, instead of the default. If set to null, trailing
65
//    punctuation will not be trimmed.
66
// 
67
// Returns:
68
// 
69
//  (String) An HTML string containing links.
70

  
71
window.linkify = (function(){
72
  var
73
    SCHEME = "[a-z\\d.-]+://",
74
    IPV4 = "(?:(?:[0-9]|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])\\.){3}(?:[0-9]|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])",
75
    HOSTNAME = "(?:(?:[^\\s!@#$%^&*()_=+[\\]{}\\\\|;:'\",.<>/?]+)\\.)+",
76
    TLD = "(?:ac|ad|aero|ae|af|ag|ai|al|am|an|ao|aq|arpa|ar|asia|as|at|au|aw|ax|az|ba|bb|bd|be|bf|bg|bh|biz|bi|bj|bm|bn|bo|br|bs|bt|bv|bw|by|bz|cat|ca|cc|cd|cf|cg|ch|ci|ck|cl|cm|cn|coop|com|co|cr|cu|cv|cx|cy|cz|de|dj|dk|dm|do|dz|ec|edu|ee|eg|er|es|et|eu|fi|fj|fk|fm|fo|fr|ga|gb|gd|ge|gf|gg|gh|gi|gl|gm|gn|gov|gp|gq|gr|gs|gt|gu|gw|gy|hk|hm|hn|hr|ht|hu|id|ie|il|im|info|int|in|io|iq|ir|is|it|je|jm|jobs|jo|jp|ke|kg|kh|ki|km|kn|kp|kr|kw|ky|kz|la|lb|lc|li|lk|lr|ls|lt|lu|lv|ly|ma|mc|md|me|mg|mh|mil|mk|ml|mm|mn|mobi|mo|mp|mq|mr|ms|mt|museum|mu|mv|mw|mx|my|mz|name|na|nc|net|ne|nf|ng|ni|nl|no|np|nr|nu|nz|om|org|pa|pe|pf|pg|ph|pk|pl|pm|pn|pro|pr|ps|pt|pw|py|qa|re|ro|rs|ru|rw|sa|sb|sc|sd|se|sg|sh|si|sj|sk|sl|sm|sn|so|sr|st|su|sv|sy|sz|tc|td|tel|tf|tg|th|tj|tk|tl|tm|tn|to|tp|travel|tr|tt|tv|tw|tz|ua|ug|uk|um|us|uy|uz|va|vc|ve|vg|vi|vn|vu|wf|ws|xn--0zwm56d|xn--11b5bs3a9aj6g|xn--80akhbyknj4f|xn--9t4b11yi5a|xn--deba0ad|xn--g6w251d|xn--hgbk6aj7f53bba|xn--hlcj6aya9esc7a|xn--jxalpdlp|xn--kgbechtv|xn--zckzah|ye|yt|yu|za|zm|zw)",
77
    HOST_OR_IP = "(?:" + HOSTNAME + TLD + "|" + IPV4 + ")",
78
    PATH = "(?:[;/][^#?<>\\s]*)?",
79
    QUERY_FRAG = "(?:\\?[^#<>\\s]*)?(?:#[^<>\\s]*)?",
80
    URI1 = "\\b" + SCHEME + "[^<>\\s]+",
81
    URI2 = "\\b" + HOST_OR_IP + PATH + QUERY_FRAG + "(?!\\w)",
82
    
83
    MAILTO = "mailto:",
84
    EMAIL = "(?:" + MAILTO + ")?[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@" + HOST_OR_IP + QUERY_FRAG + "(?!\\w)",
85
    
86
    URI_RE = new RegExp( "(?:" + URI1 + "|" + URI2 + "|" + EMAIL + ")", "ig" ),
87
    SCHEME_RE = new RegExp( "^" + SCHEME, "i" ),
88
    
89
    quotes = {
90
      "'": "`",
91
      '>': '<',
92
      ')': '(',
93
      ']': '[',
94
      '}': '{',
95
      '»': '«',
96
      '›': '‹'
97
    },
98
    
99
    default_options = {
100
      callback: function( text, href ) {
101
        return href ? '<a href="' + href + '" title="' + href + '">' + text + '</a>' : text;
102
      },
103
      punct_regexp: /(?:[!?.,:;'"]|(?:&|&amp;)(?:lt|gt|quot|apos|raquo|laquo|rsaquo|lsaquo);)$/
104
    };
105
  
106
  return function( txt, options ) {
107
    options = options || {};
108
    
109
    // Temp variables.
110
    var arr,
111
      i,
112
      link,
113
      href,
114
      
115
      // Output HTML.
116
      html = '',
117
      
118
      // Store text / link parts, in order, for re-combination.
119
      parts = [],
120
      
121
      // Used for keeping track of indices in the text.
122
      idx_prev,
123
      idx_last,
124
      idx,
125
      link_last,
126
      
127
      // Used for trimming trailing punctuation and quotes from links.
128
      matches_begin,
129
      matches_end,
130
      quote_begin,
131
      quote_end;
132
    
133
    // Initialize options.
134
    for ( i in default_options ) {
135
      if ( options[ i ] === undefined ) {
136
        options[ i ] = default_options[ i ];
137
      }
138
    }
139
    
140
    // Find links.
141
    while ( arr = URI_RE.exec( txt ) ) {
142
      
143
      link = arr[0];
144
      idx_last = URI_RE.lastIndex;
145
      idx = idx_last - link.length;
146
      
147
      // Not a link if preceded by certain characters.
148
      if ( /[\/:]/.test( txt.charAt( idx - 1 ) ) ) {
149
        continue;
150
      }
151
      
152
      // Trim trailing punctuation.
153
      do {
154
        // If no changes are made, we don't want to loop forever!
155
        link_last = link;
156
        
157
        quote_end = link.substr( -1 )
158
        quote_begin = quotes[ quote_end ];
159
        
160
        // Ending quote character?
161
        if ( quote_begin ) {
162
          matches_begin = link.match( new RegExp( '\\' + quote_begin + '(?!$)', 'g' ) );
163
          matches_end = link.match( new RegExp( '\\' + quote_end, 'g' ) );
164
          
165
          // If quotes are unbalanced, remove trailing quote character.
166
          if ( ( matches_begin ? matches_begin.length : 0 ) < ( matches_end ? matches_end.length : 0 ) ) {
167
            link = link.substr( 0, link.length - 1 );
168
            idx_last--;
169
          }
170
        }
171
        
172
        // Ending non-quote punctuation character?
173
        if ( options.punct_regexp ) {
174
          link = link.replace( options.punct_regexp, function(a){
175
            idx_last -= a.length;
176
            return '';
177
          });
178
        }
179
      } while ( link.length && link !== link_last );
180
      
181
      href = link;
182
      
183
      // Add appropriate protocol to naked links.
184
      if ( !SCHEME_RE.test( href ) ) {
185
        href = ( href.indexOf( '@' ) !== -1 ? ( !href.indexOf( MAILTO ) ? '' : MAILTO )
186
          : !href.indexOf( 'irc.' ) ? 'irc://'
187
          : !href.indexOf( 'ftp.' ) ? 'ftp://'
188
          : 'http://' )
189
          + href;
190
      }
191
      
192
      // Push preceding non-link text onto the array.
193
      if ( idx_prev != idx ) {
194
        parts.push([ txt.slice( idx_prev, idx ) ]);
195
        idx_prev = idx_last;
196
      }
197
      
198
      // Push massaged link onto the array
199
      parts.push([ link, href ]);
200
    };
201
    
202
    // Push remaining non-link text onto the array.
203
    parts.push([ txt.substr( idx_prev ) ]);
204
    
205
    // Process the array items.
206
    for ( i = 0; i < parts.length; i++ ) {
207
      html += options.callback.apply( window, parts[i] );
208
    }
209
    
210
    // In case of catastrophic failure, return the original text;
211
    return html || txt;
212
  };
213
  
214
})();
/dev/null
1
/**
2
 * Timeago is a jQuery plugin that makes it easy to support automatically
3
 * updating fuzzy timestamps (e.g. "4 minutes ago" or "about 1 day ago").
4
 *
5
 * @name timeago
6
 * @version 0.10.0
7
 * @requires jQuery v1.2.3+
8
 * @author Ryan McGeary
9
 * @license MIT License - http://www.opensource.org/licenses/mit-license.php
10
 *
11
 * For usage and examples, visit:
12
 * http://timeago.yarp.com/
13
 *
14
 * Copyright (c) 2008-2011, Ryan McGeary (ryanonjavascript -[at]- mcgeary [*dot*] org)
15
 */
16
(function($) {
17
  $.timeago = function(timestamp) {
18
    if (timestamp instanceof Date) {
19
      return inWords(timestamp);
20
    } else if (typeof timestamp === "string") {
21
      return inWords($.timeago.parse(timestamp));
22
    } else {
23
      return inWords($.timeago.datetime(timestamp));
24
    }
25
  };
26
  var $t = $.timeago;
27

  
28
  $.extend($.timeago, {
29
    settings: {
30
      refreshMillis: 60000,
31
      allowFuture: false,
32
      strings: {
33
        prefixAgo: null,
34
        prefixFromNow: null,
35
        suffixAgo: "ago",
36
        suffixFromNow: "from now",
37
        seconds: "less than a minute",
38
        minute: "about a minute",
39
        minutes: "%d minutes",
40
        hour: "about an hour",
41
        hours: "about %d hours",
42
        day: "a day",
43
        days: "%d days",
44
        month: "about a month",
45
        months: "%d months",
46
        year: "about a year",
47
        years: "%d years",
48
        numbers: []
49
      }
50
    },
51
    inWords: function(distanceMillis) {
52
      var $l = this.settings.strings;
53
      var prefix = $l.prefixAgo;
54
      var suffix = $l.suffixAgo;
55
      if (this.settings.allowFuture) {
56
        if (distanceMillis < 0) {
57
          prefix = $l.prefixFromNow;
58
          suffix = $l.suffixFromNow;
59
        }
60
      }
61

  
62
      var seconds = Math.abs(distanceMillis) / 1000;
63
      var minutes = seconds / 60;
64
      var hours = minutes / 60;
65
      var days = hours / 24;
66
      var years = days / 365;
67

  
68
      function substitute(stringOrFunction, number) {
69
        var string = $.isFunction(stringOrFunction) ? stringOrFunction(number, distanceMillis) : stringOrFunction;
70
        var value = ($l.numbers && $l.numbers[number]) || number;
71
        return string.replace(/%d/i, value);
72
      }
73

  
74
      var words = seconds < 45 && substitute($l.seconds, Math.round(seconds)) ||
75
        seconds < 90 && substitute($l.minute, 1) ||
76
        minutes < 45 && substitute($l.minutes, Math.round(minutes)) ||
77
        minutes < 90 && substitute($l.hour, 1) ||
78
        hours < 24 && substitute($l.hours, Math.round(hours)) ||
79
        hours < 48 && substitute($l.day, 1) ||
80
        days < 30 && substitute($l.days, Math.floor(days)) ||
81
        days < 60 && substitute($l.month, 1) ||
82
        days < 365 && substitute($l.months, Math.floor(days / 30)) ||
83
        years < 2 && substitute($l.year, 1) ||
84
        substitute($l.years, Math.floor(years));
85

  
86
      return $.trim([prefix, words, suffix].join(" "));
87
    },
88
    parse: function(iso8601) {
89
      var s = $.trim(iso8601);
90
      s = s.replace(/\.\d\d\d+/,""); // remove milliseconds
91
      s = s.replace(/-/,"/").replace(/-/,"/");
92
      s = s.replace(/T/," ").replace(/Z/," UTC");
93
      s = s.replace(/([\+\-]\d\d)\:?(\d\d)/," $1$2"); // -04:00 -> -0400
94
      return new Date(s);
95
    },
96
    datetime: function(elem) {
97
      // jQuery's `is()` doesn't play well with HTML5 in IE
98
      var isTime = $(elem).get(0).tagName.toLowerCase() === "time"; // $(elem).is("time");
99
      var iso8601 = isTime ? $(elem).attr("datetime") : $(elem).attr("title");
100
      return $t.parse(iso8601);
101
    }
102
  });
103

  
104
  $.fn.timeago = function() {
105
    var self = this;
106
    self.each(refresh);
107

  
108
    var $s = $t.settings;
109
    if ($s.refreshMillis > 0) {
110
      setInterval(function() { self.each(refresh); }, $s.refreshMillis);
111
    }
112
    return self;
113
  };
114

  
115
  function refresh() {
116
    var data = prepareData(this);
117
    if (!isNaN(data.datetime)) {
118
      $(this).text(inWords(data.datetime));
119
    }
120
    return this;
121
  }
122

  
123
  function prepareData(element) {
124
    element = $(element);
125
    if (!element.data("timeago")) {
126
      element.data("timeago", { datetime: $t.datetime(element) });
127
      var text = $.trim(element.text());
128
      if (text.length > 0) {
129
        element.attr("title", text);
130
      }
131
    }
132
    return element.data("timeago");
133
  }
134

  
135
  function inWords(date) {
136
    return $t.inWords(distance(date));
137
  }
138

  
139
  function distance(date) {
140
    return (new Date().getTime() - date.getTime());
141
  }
142

  
143
  // fix for IE6 suckage
144
  document.createElement("abbr");
145
  document.createElement("time");
146
}(jQuery));
/dev/null
1
/*
2
 * Twitter Search Plugin jquery.twitter.js
3
 * http://code.bocoup.com/jquery-twitter-plugin/
4
 *
5
 * Copyright (c) 2010 Bocoup, LLC
6
 * Authors: Boaz Sender, Rick Waldron, Nick Cammarata
7
 * Dual licensed under the MIT and GPL licenses.
8
 * http://code.bocoup.com/license/
9
 *
10
 */
11
var linkify = linkify || function() {};
12
;(function($, linkify) {
13

  
14
  var
15
  mention = function( str ) {
16
    return str.replace("/[@]+[A-Za-z0-9-_]+/ig", function( username ) {
17
      return username.link("https://twitter.com/"+ username.replace("@","") );
18
    });
19
  },
20
  hashtags = function( str ) {
21
    return str.replace("/[#]+[A-Za-z0-9-_]+/ig", function( tag ) {
22
      return tag.link("https://search.twitter.com/search?q="+tag.replace("#","%23"));
23
    });
24
  };
25

  
26
  $.twitter = function (options, callback) {
27
    // Fail if the options arg is not set
28
    if ( !options ) {
29
      return false;
30
    }
31

  
32
    // Set a temporary default query object
33
    var query,
34
        // Set up a string to be used later in the case that exclusions have been set
35
        exclusionsStr = "",
36
        // Set up a regex to be used later in the case that exclusions have been set
37
        exclusionsExp = new RegExp(false);
38

  
39
    // If options is a string use it as standalone query
40
    if ( typeof options === "string" ) {
41
      query = $.extend({}, $.twitter.opts, {
42
        q: options
43
      });
44
    // Else prepare the options object to be serialized
45
    } else {
46
      // If a limit is set, add it to the query object
47
      options.rpp = options.limit ? options.limit : options.rpp;
48

  
49
      // If no limit is set, make the limit the rpp
50
      options.limit = options.limit ? options.limit : options.rpp;
51

  
52
      // If there are exlusions, turn them into a regex string
53
      exclusionsStr = options.exclusions ? options.exclusions.replace(" ", "|") : false;
54

  
55
      // If there are exlusions, turn the regex string we just made into a RegExp
56
      exclusionsExp = exclusionsStr ? new RegExp( exclusionsStr ) : false;
57

  
58
      // Make a new object that is a merger of the options passed in with the default $.twitter.opts object
59
      // and assign it to the query variable
60
      query = $.extend({}, $.twitter.opts, options);
61

  
62
      // If there are exclusions, or replies or retweets are set to false, multiply the results to ask for from twitter by ten
63
      // We need to do this so that we have some meat to work with if the exclusions are common
64
      query.rpp = query.exclusions || !query.replies || !query.retweets  ? (query.rpp * 10) : query.rpp;
65

  
66
    }
67

  
68

  
69
    // Call Twitter JSONP
70
    $.getJSON("https://search.twitter.com/search.json?callback=?", query, function(tweets){
71
      callback(tweets, query, exclusionsExp);
72
    });
73
  };
74

  
75
  $.fn.twitter = function( options ) {
76
    // Fail gracefully if the options arg is not set
77
    // return the jQuery obj so that chaining does not break
78
    if ( !options ) {
79
      return this;
80
    }
81

  
82
    // Begin to iterate over the jQuery collection that the method was called on
83
    return this.each(function () {
84
      // Cache `this`
85
      var $this = $(this);
86

  
87
      $.twitter(options, function( tweets, query, exclusionsExp ) {
88
        //Create and cache a new UL
89
        var $tweets = $("<ul>"),
90
            // Create a counter variable to count up how many tweets we have rendered
91
            // unfortunately we have to do this, because exclusions, retweet booleans and replies booleans
92
            // are not supported by the Twitter Search API
93
            limitInt = 0,
94
            i;
95

  
96
        // If there are results to work with
97
        if ( tweets.results && tweets.results.length ) {
98

  
99
          //  Iterate over returned tweets
100
          for ( i in tweets.results ) {
101

  
102
            // Cache tweet content
103
            var tweet = tweets.results[i],
104
                // Set a variable to determine weather replies are set to false, and if so, weather the tweet starts with a reply
105
                allowReply = !query.replies && tweet.to_user_id ? false : true,
106
                // Set a variable to determine weather retweets are set to false, and if so, weather the tweet starts with a retweet
107
                allowRetweet = !query.retweets && tweet.text.slice(0,2) === "RT" ? false : true;
108

  
109
            // Only proceed if allow reply is false
110
            if ( !allowReply ) {
111
              continue;
112
            }
113

  
114
            // Only proceed if allow retweet is false
115
            if ( !allowRetweet ) {
116
              continue;
117
            }
118

  
119
            // If exlusions set and none of the exlusions is found in the tweet then add it to the DOM
120
            if ( exclusionsExp && exclusionsExp.test(tweet.text) ) {
121
              continue;
122
            }
123

  
124
            // Create and cache new LI
125
            var $tweet = $("<li/>", {
126
              "class": "tweet"
127
            });
128

  
129
            // Make the avatar, and append it to the $tweet
130
            if ( query.avatar === true ) {
131
              $tweet.append($("<a/>", {
132
                href: "https://twitter.com/" + tweet.from_user,
133
                html: "<img src='" + tweet.profile_image_url + "'/>"
134
              }));
135
            }
136

  
137
            // Make the tweet text, and append it to the $tweet, then to the parent
138
            $tweet.append($("<span>", {
139
              "class": "content",
140
              html: "<a href='https://twitter.com/" + tweet.from_user + "'>@" + tweet.from_user + "</a>: " + mention(hashtags(linkify(tweet.text)))
141
            }))
142
            // Append tweet to the $tweets ul
143
            .appendTo($tweets);
144

  
145
            // Count up our counter variable
146
            limitInt++;
147

  
148
            // If the counter is equal to the limit, stop rendering tweets
149
            if ( limitInt === query.limit ) {
150
              break;
151
            }
152
          }
153

  
154
          // Inject the $tweets into the DOM
155
          $this.html($tweets);
156

  
157
        // Else there are no results to work with
158
        } else {
159
          // Update the DOM to reflect that no results were found
160
          $this.html($("<h3/>", {
161
            "class": "twitter-notFound",
162
            text: query.notFoundText
163
          }));
164
        }
165
      });
166
    });
167
  };
168

  
169
  $.twitter.opts = {
170
    // Number of tweets to get
171
    // not in twitter search api, maps to and supersedes rpp (results per page)
172
    limit: 7,
173
    // Space delimited list of strings to exclude  (eg: "_ s gr @b")
174
    // not in twitter search api, done in plugin
175
    exclusions: "",
176
    // Text to display if no results are found
177
    // not in twitter search api, done in plugin
178
    notFoundText: "No results found on twitter",
179
    // Include replies?
180
    // not in twitter search api, done in plugin
181
    replies: true,
182
    // Include replies?
183
    // not in twitter search api, done in plugin
184
    retweets: true,
185
    // All of these words
186
    ands: "",
187
    // This exact phrase
188
    phrase: "",
189
    // Any of these words
190
    ors : "",
191
    // None of these words
192
    nots: "",
193
    // This hashtag
194
    tag : "",
195
    // Written in language
196
    lang: "",
197
    // From this person
198
    from: "",
199
    // To this person
200
    to: "",
201
    // Referencing this person
202
    ref: "",
203
    // Near this place
204
    near: "",
205
    // Within this distance
206
    within: "",
207
    // Distance unit (miles or kilometers)
208
    units: "",
209
    // Since this date
210
    since: "",
211
    // Until this date
212
    until: "",
213
    // Attitude: "?" or :)" or ":)"
214
    tude: "",
215
    // Containing: "links"
216
    filter: "",
217
    // Include retweet?: "retweets"
218
    include: "",
219
    // Results per page
220
    rpp: 5,
221
    // Default query
222
    q: "",
223
    // Add an avatar image of the user
224
    avatar: true
225
  };
226
}(jQuery, linkify));

Also available in: Unified diff