• R/O
  • SSH
  • HTTPS

tsubuyakiticker: コミット


コミットメタ情報

リビジョン15 (tree)
日時2013-03-23 15:04:25
作者mshio

ログメッセージ

rewrite all with coffee-script

変更サマリ

差分

--- tsubuyakiticker/tsubuyakiTicker.js (revision 14)
+++ tsubuyakiticker/tsubuyakiTicker.js (nonexistent)
@@ -1,442 +0,0 @@
1-/* Copyright (c) 2010-2011, mshio <mshio@users.sourceforge.jp>
2- * All rights reserved.
3- *
4- * Redistribution and use in source and binary forms, with or without
5- * modification, are permitted provided that the following conditions are met:
6- *
7- * o Redistributions of source code must retain the above copyright notice,
8- * this list of conditions and the following disclaimer.
9- * o Redistributions in binary form must reproduce the above copyright notice,
10- * this list of conditions and the following disclaimer in the documentation
11- * and/or other materials provided with the distribution.
12- * o Neither the name of the Tsubuyaki Ticker Project nor the names of its
13- * contributors may be used to endorse or promote products derived from this
14- * software without specific prior written permission.
15- *
16- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26- * POSSIBILITY OF SUCH DAMAGE.
27- */
28-function TsubuyakiTicker(settings) {
29- var _ = TsubuyakiTicker;
30- var tickerData = {
31- userList: [],
32- tweetList: {},
33- tweetNum: {},
34- failure: {},
35- animation: null,
36- dateFormatter: null
37- };
38-
39- /* div element that has id of 'tsubuyakiTicker' */
40- var target;
41-
42- /* callback function receivers */
43- _.callbacks = { };
44- /* version */
45- _.version = '0.0.06';
46-
47- function Tweet(user, text, id, date, lang) {
48- this.user = user;
49- this.text =
50- text.replace(/(http:\/\/[0-9A-Za-z\.\/_\?&\~\-\=\#\:%]+)/g, '<a href="$1" target="_blank">$1</a>')
51- .replace(/(\s|[^a-zA-Z0-9])(@[a-zA-Z0-9_]+)/g, '$1<a href="http://twitter.com/$2" target="_blank">$2</a>')
52- .replace(/^(@[a-zA-Z0-9_]+)/, '<a href="http://twitter.com/$1" target="_blank">$1</a>')
53- .replace(/\s(#[a-zA-Z0-9_]+)/g, ' <a href="http://twitter.com/search?q=$1" target="_blank">$1</a>')
54- .replace(/^(#[a-zA-Z0-9_]+)/, '<a href="http://twitter.com/search?q=$1" target="_blank">$1</a>');
55- this.id = id;
56- this.date = date;
57- this.lang = lang;
58- }
59-
60- function ListManager(numUser) {
61- this.numUser = numUser;
62- this.counter = 0;
63- this.timer = new TickerTimer(this, 'execCheck', 10);
64- }
65- ListManager.prototype = {
66- check: function() {
67- var l = tickerData.tweetList;
68- var n = tickerData.tweetNum;
69- var f = tickerData.failure;
70- var c = (function() {
71- var r = 0;
72- for (var i in f) if (f[i]) r++;
73- return r;
74- })();
75- var size = 0;
76- for (var k in l) { size++; }
77- if (size != this.numUser - c) return false;
78- for (var i in l) if (l[i].length != n[i]) return false;
79- for (var i in f) tickerData.failure[i] = false;
80- return true;
81- },
82- execCheck: function() {
83- if (! this.check()) {
84- if (++this.counter < 200) this.timer.restart(10);
85- else formatList(settings.responseFailureMessage);
86- } else {
87- cleanupTickerDiv();
88- formatList(null);
89- }
90- }
91- };
92-
93- function TickerTimer(target, method, timeout) {
94- this.target = target;
95- this.method = method;
96- this.id = this.setTimer(timeout);
97- }
98- TickerTimer.prototype = {
99- abort: function() { if (this.id) { clearTimeout(this.id); } },
100- restart: function(timeout) { this.id = this.setTimer(timeout); },
101- setTimer: function(timeout) {
102- var t = this.target, m = this.method;
103- return setTimeout(function() { if (t[m]) { t[m](); } }, timeout);
104- }
105- };
106-
107- function DateFormatter() {
108- this.scenario = [];
109- }
110- DateFormatter.prototype = {
111- apply: function(text) {
112- var r = [];
113- var d = new Date(text);
114- var s = this.scenario;
115- for (var i in s) {
116- if (s[i][0] == 'put') {
117- r.push(s[i][1]);
118- } else {
119- var buf = d[s[i][0]]();
120- for (var j = 1; j < s[i].length; j++)
121- buf = this[s[i][j]](buf);
122- r.push(buf);
123- }
124- }
125- return r.join('');
126- },
127- plus: function(value) {
128- return parseInt(value) + 1;
129- },
130- fmtZero: function(value) {
131- return (String(value).length == 1) ? '0' + value : value;
132- },
133- getDblFig: function(value) {
134- var s = String(value);
135- return s.substring(s.length - 2);
136- },
137- compile: function(format) {
138- var p = []
139- var status = 'normal';
140- var start = 0;
141- var c;
142-
143- var map = { 'M': 'getMonth', 'D': 'getDate', 'h': 'getHours',
144- 'm': 'getMinutes', 's': 'getSeconds' };
145-
146- function changeStatus(i) {
147- var len = i - start;
148- if (status != c) {
149- switch (status) {
150- case 'normal':
151- if (i != 0)
152- p.push(['put', format.substring(start, i)]);
153- break;
154- case 'Y':
155- if (len == 2) p.push(['getYear', 'getDblFig']);
156- else if (len == 4) p.push(['getFullYear']);
157- else p.push(['put', format.substring(start, i)]);
158- break;
159- default:
160- var m = map[status];
161- if (len == 1) {
162- if (status == 'M') p.push([m, 'plus']);
163- else p.push([m]);
164- } else if (len == 2) {
165- if (status == 'M') p.push([m, 'plus', 'fmtZero']);
166- else p.push([m, 'fmtZero']);
167- } else p.push(['put', format.substring(start, i)]);
168- break;
169- }
170- start = i;
171- }
172- }
173-
174- for (var i = 0; i < format.length; i++) {
175- c = format.charAt(i);
176- if (c != 'Y' && c != 'M' && c != 'D' &&
177- c != 'h' && c != 'm' && c != 's') c = 'normal';
178- changeStatus(i);
179- status = c;
180- }
181- c = '';
182- changeStatus(format.length);
183- this.scenario = p;
184- }
185- }
186-
187- function TickerAnimation(span) {
188- this.span = span;
189- this.interval = settings.interval;
190- this.movingQuantity = settings.movingQuantity;
191- this.speeddown = 0;
192- this.prepare = false;
193- this.timer = new TickerTimer(this, 'move', this.interval);
194- }
195- TickerAnimation.prototype = {
196- move: function() {
197- var s = this.span;
198- var x = getIntValue(s, s.style.left);
199- var w = s.offsetWidth;
200- if (this.speeddown != 0) this.speeddown += this.speeddown;
201- var v = this.movingQuantity - this.speeddown;
202- var auto = checkSwitch(settings.autoReload);
203- if (v > 0) {
204- x -= v;
205- if (x < -w) {
206- x = getTickerWidth();
207- if (this.span.prepare)
208- this.span.innerHTML = this.span.prepare;
209- this.prepare = false;
210- } else if (auto && ! this.prepare && x < - w + 250) {
211- this.getTweetsInBackground();
212- }
213- s.style.left = x + (s.measure ? s.measure : 'px');
214- this.timer.restart(this.interval);
215- } else this.timer.abort();
216- },
217- getTweetsInBackground: function() {
218- var l = tickerData.userList;
219- var n = settings.numberOfRecord;
220- this.prepare = true;
221- for (var i in l) { _._getTweets(l[i], n, getNumberCode(i)); }
222- new ListManager(l.length);
223- }
224- };
225-
226- _._getTweets = function(account, numOfRec, numCode) {
227- var s = document.createElement('script');
228- s.type = 'text/javascript';
229- s.src = 'http://search.twitter.com/search.json?q=from%3A' + account + '&rpp=' + numOfRec + '&callback=TsubuyakiTicker.callbacks.' + numCode;
230- target.appendChild(s);
231- };
232-
233- /*
234- * sets default values for the dictionary 'settings' to the parameter
235- * and returns it
236- */
237- function init_settings(s) {
238- var def = {
239- users: null,
240- numberOfRecord: 5,
241- interval: 100,
242- movingQuantity: 7,
243- dateFormat: 'YYYY/MM/DD hh:mm',
244- displayUserID: 'on',
245- displayDateTime: 'on',
246- autoReload: 'off',
247- notFoundMessage: '* * * [INFO] NO TWEET WAS FOUND * * *',
248- responseFailureMessage: '* * * [TIMEOUT] THERE IS AT LEAST ONE RESPONSE NOT BEING RETURNED * * *'
249- };
250- for (var k in def) s[k] = s[k] || def[k];
251- return s;
252- }
253-
254- /*
255- * removes alphabet letters and gets integer value
256- * from string fotmatted by a number and an unit.
257- */
258- function getIntValue(element, text) {
259- var v = text.replace(/([0-9]+)\s*[a-zA-Z]+/, '$1');
260- if (v.length != text) {
261- element.measure = text.substring(v.length);
262- return parseInt(v);
263- }
264- return null;
265- }
266-
267- function getTickerWidth() {
268- var s = target.style;
269- return (s.width) ? getIntValue(target, s.width) : target.offsetWidth;
270- }
271-
272- function setupDiv(element) {
273- var d = element;
274- d.style.overflow = 'hidden';
275- var w = getTickerWidth();
276- var c = document.createElement('span');
277- var s = c.style;
278- s.whiteSpace = 'nowrap';
279- s.position = 'relative';
280- if (w) {
281- s.left = w + (d.measure ? d.measure : 'px');
282- d.appendChild(c);
283- }
284- }
285-
286- function getNumberCode(number) {
287- var n = number;
288- var s = [];
289- do {
290- var v = n % 10;
291- n = (n - v) / 10;
292- s.push(String.fromCharCode(v + 0x61));
293- } while (n > 0);
294- return s.join('');
295- }
296-
297- function cleanupTickerDiv() {
298- var ns = target.childNodes;
299- for (var i in ns) {
300- var n = ns[i] ? ns[i].tagName : null;
301- if (n && (n == 'script' || n == 'SCRIPT'))
302- target.removeChild(ns[i]);
303- }
304- }
305-
306- function formatList(enforcedMessage) {
307- function toDateFmt(text) {
308- if (! tickerData.dateFormatter)
309- (tickerData.dateFormatter =
310- new DateFormatter()).compile(settings.dateFormat);
311- return tickerData.dateFormatter.apply(text);
312- }
313-
314- function getUnitedList() {
315- var l = [];
316- var t = tickerData.tweetList;
317- for (var k in t) for (var i in t[k]) l.push(t[k][i]);
318- l.sort(function(a, b) { return b.id - a.id; });
319- return l;
320- }
321-
322- function makeupTweetLine(list, showDatetime, showUser) {
323- var a = [];
324- for (var n = 0; n < tickerData.tweetNum[list[0].user]; n++) {
325- var s = [];
326- s.push('<span class="tsubu-tweet" lang="');
327- s.push(list[n].lang);
328- s.push('">');
329- s.push(list[n].text);
330- s.push('</span>');
331- if (showDatetime || showUser) {
332- s.push('&nbsp;(');
333- if (showDatetime) {
334- s.push('<span class="tsubu-datetime">');
335- s.push(toDateFmt(list[n].date));
336- s.push('</span>');
337- if (showUser) { s.push('&nbsp;'); }
338- }
339- if (showUser) {
340- s.push('<a class="tsubu-user" href="http://twitter.com/');
341- s.push(list[n].user);
342- s.push('" target="_blank">');
343- s.push(list[n].user);
344- s.push('</a>');
345- }
346- s.push(')');
347- }
348- a.push(s.join(''));
349- }
350- return a.join('<span class="tsubu-sep">&nbsp;&diams;&nbsp;&diams;&nbsp;&diams;&nbsp;</span>&nbsp;');
351- }
352-
353- function getErrorLine(message) {
354- return '<span class="tsubu-error">' + message + '</span>';
355- }
356-
357- var buf = enforcedMessage ? getErrorLine(enforcedMessage) :
358- (function() {
359- var showUser = checkSwitch(settings.displayUserID);
360- var showDate = checkSwitch(settings.displayDateTime);
361- var list = getUnitedList();
362- return (list.length > 0) ?
363- makeupTweetLine(list, showDate, showUser) :
364- getErrorLine(settings.notFoundMessage);
365- })();
366- var d = target.childNodes[0];
367- if (d.innerHTML.length > 0) {
368- d.prepare = buf;
369- } else {
370- d.innerHTML = buf;
371- tickerData.animation = new TickerAnimation(d);
372- }
373- }
374-
375- function checkSwitch(v) {
376- return v != 'off' && v != 'Off' && v != 'OFF';
377- }
378-
379- _.tweets = function(result, code) {
380- var r = result.results;
381- if (r.length > 0) {
382- tickerData.tweetList[r[0].from_user] = [];
383- tickerData.tweetNum[r[0].from_user] = r.length;
384- var l = tickerData.tweetList[r[0].from_user];
385- for (var i in r) l.push(new Tweet(r[i].from_user, r[i].text, r[i].id, r[i].created_at, r[i].iso_language_code));
386- } else {
387- tickerData.failure[code] = true;
388- }
389- }
390-
391- _.stopTickerAnimation = function() {
392- var a = tickerData.animation;
393- if (a && a.speeddown == 0) { a.speeddown = 0.05; }
394- };
395-
396- _.restartTickerAnimation = function() {
397- var a = tickerData.animation;
398- if (a) { a.speeddown = 0; }
399- if (a.timer) { a.timer.abort(); }
400- a.timer = new TickerTimer(a, 'move', a.interval);
401- };
402-
403- _.main = function() {
404- settings = init_settings(settings);
405-
406- function set_callback(i, n) {
407- var c = getNumberCode(i);
408- tickerData.failure[c] = false;
409- _.callbacks[c] =
410- (function() {
411- var p = c;
412- return function(r) { _.tweets(r, p) };
413- })();
414- _._getTweets(tickerData.userList[i], n, c);
415- }
416-
417- target = document.getElementById('tsubuyakiTicker');
418- var s = settings.users;
419- if (target && s && s != '') {
420- setupDiv(target);
421- var u = s.substring(0, 160);
422- var l = u.split(/,\s*/);
423- var n = settings.numberOfRecord;
424- try {
425- n = parseInt(String(n));
426- for (var i in l) {
427- tickerData.userList[i] = encodeURI(l[i]);
428- set_callback(i, n);
429- }
430- new ListManager(l.length, n);
431- } catch (e) { }
432- }
433- };
434-
435- (function() {
436- var m = TsubuyakiTicker.main
437- if (window.addEventListener) window.addEventListener('load', m, false);
438- else if (window.attachEvent) window.attachEvent('onload', m);
439- })();
440-
441- return _;
442-}
Deleted: svn:executable
## -1 +0,0 ##
-*
\ No newline at end of property
--- tsubuyakiticker/test.html (nonexistent)
+++ tsubuyakiticker/test.html (revision 15)
@@ -0,0 +1,13 @@
1+<html>
2+<head>
3+<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
4+<title>test</title>
5+<script type="text/javascript" src="js/tsubuyakiTicker.mini.js"></script>
6+<script type="text/javascript">
7+TsubuyakiTicker({users: 'mshio, tenkijp'});
8+</script>
9+</head>
10+<body>
11+<div id="tsubuyakiTicker"></div>
12+</body>
13+</html>
--- tsubuyakiticker/src/tickerData.coffee (nonexistent)
+++ tsubuyakiticker/src/tickerData.coffee (revision 15)
@@ -0,0 +1,19 @@
1+class TickerData
2+ constructor: (setting) ->
3+ @userList = this._makeUserListFrom setting
4+ @failure = {}
5+ @tweetList = {}
6+ @tweetNum = {}
7+ @animation = null
8+ @dateFormatter = null
9+
10+ _makeUserListFrom: (setting) ->
11+ list = []
12+ s = setting.users
13+ return [] if ! s or s is ''
14+
15+ items = (s.substring 0, 160).split /,\s*/
16+ try
17+ list.push encodeURI it for it in items
18+ catch e
19+ return list
--- tsubuyakiticker/src/browserDetector.coffee (nonexistent)
+++ tsubuyakiticker/src/browserDetector.coffee (revision 15)
@@ -0,0 +1,22 @@
1+class Browser
2+ constructor: ->
3+ @userAgent = window.navigator.userAgent if window
4+ @names = Browser.names
5+
6+ name: ->
7+ return 'other' if ! @userAgent
8+ ua = @userAgent.toLowerCase()
9+ for nm in @names
10+ return nm if (ua.indexOf nm) isnt -1
11+ return 'other'
12+
13+ canAcceptIntrinsicWidth: ->
14+ return this.name() is 'chrome'
15+
16+Browser.names = [
17+ 'ie',
18+ 'chrome',
19+ 'safari',
20+ 'gecko',
21+ 'opera',
22+ ]
--- tsubuyakiticker/src/tickerDomWriter.coffee (nonexistent)
+++ tsubuyakiticker/src/tickerDomWriter.coffee (revision 15)
@@ -0,0 +1,110 @@
1+class TickerDomWriter
2+ # generate TickerDomWriter object.
3+ #
4+ # arguments
5+ # ------------------------------
6+ # - @target : target DIV element.
7+ # - _public : a public object to bind variables and
8+ # functions or to get them.
9+ constructor: (@target, _public) ->
10+ @_ = _public
11+ @setting = @_.setting
12+ @tickerData = @_.tickerData
13+
14+ # clean up child nodes of the target DIV element.
15+ cleanup: ->
16+ nds = @target.childNodes
17+ for nd in nds
18+ nm = if nd then nd.tagName else null
19+ @target.removeChild nd if nm and (nm is 'script' or nm is 'SCRIPT')
20+
21+ generateList: (errorMessage) ->
22+ nodes = this._listNodes errorMessage
23+ d = @target.childNodes[0]
24+ if d.childNodes.length > 0
25+ d.prepare = nodes
26+ else
27+ for node in nodes
28+ d.appendChild node
29+ @tickerData.animation = new TickerAnimation d, @target, @_
30+
31+ _listNodes: (errorMessage) ->
32+ nodes = null
33+ if errorMessage
34+ nodes = this._errorLine errorMessage
35+ else
36+ showUser = checkSwitch @setting.displayUserID
37+ showDate = checkSwitch @setting.displayDateTime
38+ list = this._unitedList()
39+ if list.length > 0
40+ nodes = this.makeupTweetLine list, showDate, showUser
41+ else
42+ nodes = this._errorLine @setting.notFoundMessage
43+ return nodes
44+
45+ _errorList: (errorMessage) ->
46+ span = document.createElement 'span'
47+ span.setAttribute "class", "tsubu-error"
48+ text = document.createTextNode errorMessage
49+ span.appendChild text
50+ return [span]
51+
52+ _unitedList: ->
53+ l = []
54+ for k, v of @tickerData.tweetList
55+ l.push t for t in v
56+ l.sort (a, b) ->
57+ b.id - a.id
58+ return l
59+
60+ _tweetSpan: (list) ->
61+ span = document.createElement 'span'
62+ span.setAttribute 'class', 'tsubu-tweet'
63+ span.setAttribute 'lang', list.lang
64+ span.innerHTML = list.text
65+ return span
66+
67+ _datetimeSpan: (list) ->
68+ span = document.createElement 'span'
69+ span.setAttribute 'class', 'tsubu-datetime'
70+ text = document.createTextNode this._toDateFmt list.date
71+ span.appendChild text
72+ return span
73+
74+ _userSpan: (list) ->
75+ span = document.createElement 'a'
76+ span.setAttribute 'class', 'tsubu-user'
77+ span.setAttribute 'href', "http://twitter.com/#{list.user}"
78+ span.setAttribute 'target', '_blank'
79+ text = document.createTextNode list.user
80+ span.appendChild text
81+ return span
82+
83+ _intervalSpan: ->
84+ span = document.createElement 'span'
85+ span.setAttribute 'class', 'tsubu-sep'
86+ diams = []
87+ diams.push '&nbsp;' for i in [0..4]
88+ span.innerHTML = diams.join '&diams;'
89+ return span
90+
91+
92+ makeupTweetLine: (list, showDatetime, showUser) ->
93+ nodes = []
94+ for n in [0..@tickerData.tweetNum[list[0].user]]
95+ nodes.push this._tweetSpan list[n]
96+ nodes.push document.createTextNode ' (' if showDatetime or showUser
97+ nodes.push this._datetimeSpan list[n] if showDatetime
98+ nodes.push document.createTextNode ' ' if showDatetime and showUser
99+ nodes.push this._userSpan list[n] if showUser
100+ nodes.push document.createTextNode ')' if showDatetime or showUser
101+ if n != @tickerData.tweetNum[list[0].user] - 1
102+ nodes.push this._intervalSpan()
103+ nodes.push document.createTextNode ' '
104+ return nodes
105+
106+ _toDateFmt: (text) ->
107+ if ! @tickerData.dateFormatter
108+ @tickerData.dateFormatter = new DateFormatter()
109+ @tickerData.dateFormatter.compile @setting.dateFormat
110+ return @tickerData.dateFormatter.apply text
--- tsubuyakiticker/src/tickerSetting.coffee (nonexistent)
+++ tsubuyakiticker/src/tickerSetting.coffee (revision 15)
@@ -0,0 +1,21 @@
1+class TickerSetting
2+ constructor: (userSetting) ->
3+ s = this._mergeUserSetting userSetting
4+ this[k] = v for k, v of s
5+
6+ _mergeUserSetting: (setting) ->
7+ s = TickerSetting.defaultSetting
8+ setting[k] = setting[k] || s[k] for k, v of s
9+ return setting
10+
11+TickerSetting.defaultSetting =
12+ users: null
13+ numberOfRecord: 5
14+ interval: 100
15+ movingQuantity: 7
16+ dateFormat: 'YYYY/MM/DD hh:mm'
17+ displayUserID: 'on'
18+ displayDateTime: 'on'
19+ autoReload: 'off'
20+ notFoundMessage: '[INFO] NO TWEET WAS FOUND'
21+ responseFailureMessage: '[TIMEOUT] SOME RESPONSES WERE NOT RETURNED'
--- tsubuyakiticker/src/tweetCollector.coffee (nonexistent)
+++ tsubuyakiticker/src/tweetCollector.coffee (revision 15)
@@ -0,0 +1,55 @@
1+class TweetCollector
2+ constructor: (@target, @_) ->
3+
4+ collect: ->
5+ i = -1
6+ num = @_.setting.numberOfRecord
7+ for user in @_.tickerData.userList
8+ this.setCallerAndCallback ++i, num
9+
10+ setCallerAndCallback: (index, numberOfRecord) ->
11+ c = this._numberCode index
12+ _ = @_
13+ @_.tickerData.failure[c] = false
14+ @_.callbacks[c] = (->
15+ p = c
16+ return (r) ->
17+ _.tweets r, p)();
18+ this.setCaller @_.tickerData.userList[index], numberOfRecord, c
19+
20+ setCaller: (account, numOfRec, numCode) ->
21+ s = document.createElement 'script'
22+ s.type = 'text/javascript'
23+ s.src = this._makeupAPIURL
24+ query: "from%3A#{account}"
25+ numberOfRecord: numOfRec
26+ callback: "TsubuyakiTicker.callbacks.#{numCode}"
27+ @target.appendChild s
28+
29+ _makeupAPIURL: (paramSetting) ->
30+ s = TweetCollector.setting
31+ params = this._queryString paramSetting, s.paramKeys
32+ "#{s.host}#{s.restAPI}?#{params}"
33+
34+ _queryString: (paramSetting, keys) ->
35+ items = []
36+ items.push "#{keys[k]}=#{v}" for k, v of paramSetting
37+ items.join '&'
38+
39+ _numberCode: (num) ->
40+ n = num
41+ s = []
42+ while true
43+ v = n % 10
44+ n = (n - v) / 10
45+ s.push String.fromCharCode(v + 0x61)
46+ break if n <= 0
47+ s.join ''
48+
49+TweetCollector.setting =
50+ host: "http://search.twitter.com/"
51+ restAPI: "search.json"
52+ paramKeys:
53+ query: "q"
54+ numberOfRecord: "rpp"
55+ callback: "callback"
--- tsubuyakiticker/src/dateFormatter.coffee (nonexistent)
+++ tsubuyakiticker/src/dateFormatter.coffee (revision 15)
@@ -0,0 +1,78 @@
1+class DateFormatter
2+ constructor: ->
3+ @scenario = []
4+
5+ apply: (text) ->
6+ r = []
7+ d = new Date text
8+ for s in @scenario
9+ if s[0] is 'put'
10+ r.push s[1]
11+ else
12+ buf = d[s[0]]() if s[0]
13+ for j in [1...s.length]
14+ buf = this[s[j]] buf if s[j]
15+ r.push buf
16+ r.join ''
17+
18+ plus: (value) ->
19+ (parseInt value) + 1
20+
21+ fmtZero: (value) ->
22+ if (String value).length is 1 then "0#{value}" else value
23+
24+ getDblFig: (value) ->
25+ s = String value
26+ s.substring(s.length - 2)
27+
28+ compile: (format) ->
29+ p = []
30+ status = 'normal'
31+ start = 0
32+ map =
33+ 'M': 'getMonth'
34+ 'D': 'getDate'
35+ 'h': 'getHours'
36+ 'm': 'getMinutes'
37+ 's': 'getSeconds'
38+
39+ changeStatus = (i) ->
40+ len = i - start
41+ if status != c
42+ switch status
43+ when 'normal'
44+ p.push ['put', format.substring start, i] if i != 0
45+ when 'Y'
46+ if len == 2
47+ p.push ['getYear', 'getDblFig']
48+ else if len == 4
49+ p.push ['getFullYear']
50+ else
51+ p.push ['put', format.substring start, i]
52+ else
53+ m = map[status]
54+ if len == 1
55+ if status == 'M'
56+ p.push [m, 'plus']
57+ else
58+ p.push [m]
59+ else if len == 2
60+ if status == 'M'
61+ p.push [m, 'plus', 'fmtZero']
62+ else
63+ p.push [m, 'fmtZero']
64+ else
65+ p.push ['put', format.substring start, i]
66+ start = i
67+
68+ c = null
69+ for i in [0...format.length]
70+ c = format.charAt i
71+ if ! (c in ['Y', 'M', 'D', 'h', 'm', 's'])
72+ c = 'normal'
73+ changeStatus i
74+ status = c
75+
76+ c = ''
77+ changeStatus format.length
78+ @scenario = p
--- tsubuyakiticker/src/tickerAnimation.coffee (nonexistent)
+++ tsubuyakiticker/src/tickerAnimation.coffee (revision 15)
@@ -0,0 +1,35 @@
1+class TickerAnimation
2+ constructor: (@span, @target, @_) ->
3+ @interval = @_.setting.interval
4+ @movingQuantity = @_.setting.movingQuantity
5+ @speeddown = 0
6+ @prepare = false
7+ @timer = new TickerTimer this, 'move', @interval
8+
9+ move: ->
10+ x = parseInt @span.style.left
11+ w = @span.offsetWidth
12+ @speeddown += @speeddown if @speeddown != 0
13+ v = @movingQuantity - @speeddown
14+ auto = checkSwitch @_.setting.autoReload
15+ if v > 0
16+ x -= v
17+ if x < -w
18+ x = if @target.style.width then parseInt(@target.style.width) else @target.offsetWidth
19+ if @span.prepare
20+ @span.appendChild node for node in @span.prepare
21+ @span.prepare = false
22+ else if auto and ! @prepare and x < - w + 250
23+ this.getTweetsInBackground()
24+ @span.style.left = x + (@span.measure || 'px')
25+ @timer.restart @interval
26+ else
27+ @timer.abort()
28+
29+ getTweetsInBackground: ->
30+ l = @_.tickerData.userList
31+ n = @_.setting.numberOfRecord
32+ @prepare = true
33+ c = new TweetCollector @target, @_
34+ c.setCaller l[i], n, c._numberCode i for i in [0..l.length]
35+ new ListManager l.length
--- tsubuyakiticker/src/tickerTimer.coffee (nonexistent)
+++ tsubuyakiticker/src/tickerTimer.coffee (revision 15)
@@ -0,0 +1,17 @@
1+class TickerTimer
2+ constructor: (@target, @method, timeout) ->
3+ @id = this.setTimer timeout
4+
5+ abort: ->
6+ clearTimeout @id
7+
8+ restart: (timeout) ->
9+ @id = this.setTimer(timeout)
10+
11+ setTimer: (timeout) ->
12+ t = @target
13+ m = @method
14+ f = ->
15+ t[m] if t[m]()
16+ return setTimeout f, timeout
17+
--- tsubuyakiticker/src/tsubuyakiTicker.coffee (nonexistent)
+++ tsubuyakiticker/src/tsubuyakiTicker.coffee (revision 15)
@@ -0,0 +1,48 @@
1+_pub = null
2+
3+tweets = (result, code) ->
4+ d = _pub.tickerData
5+ r = result.results
6+ if r.length > 0
7+ d.tweetList[r[0].from_user] = []
8+ d.tweetNum[r[0].from_user] = r.length
9+ l = d.tweetList[r[0].from_user]
10+ for rec in r
11+ l.push new Tweet rec.from_user, rec.text,
12+ rec.id, rec.created_at,
13+ rec.iso_language_code
14+ else
15+ d.failure[code] = true
16+
17+checkSwitch = (value) ->
18+ value.toLowerCase() isnt 'off'
19+
20+run = ->
21+ browser = new Browser()
22+
23+ target = document.getElementById 'tsubuyakiTicker'
24+ b = new TickerDomBuilder target, browser
25+ b.addNoWrapElement()
26+
27+ _pub = window.TsubuyakiTicker
28+
29+ _pub.tickerDomWriter = new TickerDomWriter target, _pub
30+
31+ c = new TweetCollector target, _pub
32+ c.collect()
33+
34+ new ListManager _pub.tickerData.userList.length, _pub
35+
36+# main --
37+if window
38+ window.TsubuyakiTicker = (setting) ->
39+ _pub = window.TsubuyakiTicker
40+ _pub.tweets = tweets
41+ _pub.callbacks = {}
42+ _pub.setting = new TickerSetting setting
43+ _pub.tickerData = new TickerData _pub.setting
44+
45+ if window.addEventListener
46+ window.addEventListener 'load', run, false
47+ else if window.attachEvent
48+ window.attachEvent 'onload', run
--- tsubuyakiticker/src/listManager.coffee (nonexistent)
+++ tsubuyakiticker/src/listManager.coffee (revision 15)
@@ -0,0 +1,37 @@
1+class ListManager
2+ constructor: (@numUser, _public) ->
3+ @_ = _public
4+ @counter = 0
5+ @timer = new TickerTimer this, 'execCheck', 10
6+ @writer = _public.tickerDomWriter
7+ @setting = _public.setting
8+
9+ check: ->
10+ data = @_.tickerData
11+ list = data.tweetList
12+ num = data.tweetNum
13+ failure = data.failure
14+ failureCount = (->
15+ r = 0
16+ for k, v of failure
17+ r++ if v
18+ return r)()
19+
20+ size = 0
21+ size++ for k, t of list
22+ return false if size != @numUser - failureCount
23+ i = 0
24+ for k, t of list
25+ return false if t.length != num[k]
26+ data.failure[k] = false for k, t of failure
27+ return true
28+
29+ execCheck: ->
30+ if ! this.check()
31+ if ++@counter < 200
32+ @timer.restart 10
33+ else
34+ @writer.generateList @setting.responseFailureMessage
35+ else
36+ @writer.cleanup()
37+ @writer.generateList null
--- tsubuyakiticker/src/tickerDomBuilder.coffee (nonexistent)
+++ tsubuyakiticker/src/tickerDomBuilder.coffee (revision 15)
@@ -0,0 +1,59 @@
1+###
2+TickerDomBuilder
3+--------------------------------------------------
4+A class to make up a DIV element for the tsubuyaki ticker.
5+
6+[usage]
7+STEP 1. get an instance.
8+ b = new TickerDomBuilder element, browser
9+
10+STEP 2. add a span with the no-wrap style
11+ b.addNoWrapElement()
12+###
13+class TickerDomBuilder
14+ constructor: (@target, @browser) ->
15+ @target.style.overflow = 'hidden'
16+ @currentTarget = @target
17+
18+ addNoWrapElement: ->
19+ util = TickerDomUtil
20+ style =
21+ whiteSpace: 'pre'
22+ position: 'relative'
23+ left: util.spanLeft @target
24+ elem = null
25+ if @browser.canAcceptIntrinsicWidth()
26+ elem = document.createElement 'div'
27+ style['width'] = 'intrinsic'
28+ else
29+ elem = document.createElement 'span'
30+
31+ for k, v of style
32+ elem.style[k] = v if v
33+
34+ @currentTarget.appendChild elem
35+ @currentTarget = elem
36+
37+ addTextNode: (text) ->
38+ t = document.createTextNode text
39+ @currentTarget.appendChild t
40+
41+
42+TickerDomUtil =
43+ # get a unit using for the width of the specified element
44+ measureOf: (element) ->
45+ reg = /^[0-9\.]+\s*([a-zA-Z]+)$/
46+ w = element.style.width
47+ if w and w.match reg then w.replace reg, '$1' else null
48+
49+ # get a number for the width of the specified element
50+ widthOf: (element) ->
51+ s = element.style
52+ if s.width then parseInt s.width else element.offsetWidth
53+
54+ # get initial left position for the target span
55+ spanLeft: (element) ->
56+ u = TickerDomUtil
57+ m = u.measureOf element
58+ w = u.widthOf element
59+ w + (if m then m else 'px')
--- tsubuyakiticker/src/tweet.coffee (nonexistent)
+++ tsubuyakiticker/src/tweet.coffee (revision 15)
@@ -0,0 +1,45 @@
1+class Tweet
2+ constructor: (@user, text, @id, @date, @lang) ->
3+ @text = this._linkedText(text)
4+
5+ _linkedText: (text) ->
6+ keys = []
7+ keys.push k for k, v of TweetTextReplacer.REGEXPS
8+ replacer = new TweetTextReplacer
9+ for k in keys
10+ text = replacer[k](text)
11+ text
12+
13+
14+class TweetTextReplacer
15+ constructor: ->
16+ @reg = TweetTextReplacer.REGEXPS
17+
18+ linkTag: (href, content) ->
19+ "<a href=\"#{href}\" target=\"_blank\">#{content}</a>"
20+
21+ link: (text) ->
22+ text.replace @reg['link'], this.linkTag('$1', '$1')
23+
24+ account0: (text) ->
25+ href = "http://twitter.com/$2"
26+ text.replace @reg['account0'], "$1#{this.linkTag(href, '$2')}"
27+
28+ account1: (text) ->
29+ href = "http://twitter.com/$1"
30+ text.replace @reg['account1'], this.linkTag(href, '$1')
31+
32+ hash0: (text) ->
33+ href = "http://twitter.com/search?q=$2"
34+ text.replace @reg['hash0'], "$1#{this.linkTag(href, '$2')}"
35+
36+ hash1: (text) ->
37+ href = "http://twitter.com/search?q=$1"
38+ text.replace @reg['hash1'], this.linkTag(href, '$1')
39+
40+TweetTextReplacer.REGEXPS =
41+ link: /(http:\/\/[0-9A-Za-z\.\/_\?&\~\-\=\#\:%]+)/g
42+ account0: /(\s|[^a-zA-Z0-9])(@[a-zA-Z0-9_]+)/g
43+ account1: /^(@[a-zA-Z0-9_]+)/
44+ hash0: /(\s)(\#[a-zA-Z0-9_]+)/g
45+ hash1: /^(\#[a-zA-Z0-9_]+)/
--- tsubuyakiticker/js/tsubuyakiTicker.mini.js (nonexistent)
+++ tsubuyakiticker/js/tsubuyakiTicker.mini.js (revision 15)
@@ -0,0 +1,23 @@
1+(function(){var r,x,s,y,z,A,t,B,u,v,C,n,p,q,w,D,k;r=function(){function b(){window&&(this.userAgent=window.navigator.userAgent);this.names=b.names}b.prototype.name=function(){var a,c,d,b,e;if(!this.userAgent)return"other";c=this.userAgent.toLowerCase();e=this.names;d=0;for(b=e.length;d<b;d++)if(a=e[d],-1!==c.indexOf(a))return a;return"other"};b.prototype.canAcceptIntrinsicWidth=function(){return"chrome"===this.name()};return b}();r.names=["ie","chrome","safari","gecko","opera"];x=function(){function b(){this.scenario=
2+[]}b.prototype.apply=function(a){var c,d,b,e,g,h,k,m,l;b=[];a=new Date(a);m=this.scenario;g=0;for(k=m.length;g<k;g++)if(e=m[g],"put"===e[0])b.push(e[1]);else{e[0]&&(c=a[e[0]]());d=h=1;for(l=e.length;1<=l?h<l:h>l;d=1<=l?++h:--h)e[d]&&(c=this[e[d]](c));b.push(c)}return b.join("")};b.prototype.plus=function(a){return parseInt(a)+1};b.prototype.fmtZero=function(a){return 1===String(a).length?"0"+a:a};b.prototype.getDblFig=function(a){a=String(a);return a.substring(a.length-2)};b.prototype.compile=function(a){var c,
3+d,b,e,g,h,k,m,l;g=[];k="normal";h=0;e={M:"getMonth",D:"getDate",h:"getHours",m:"getMinutes",s:"getSeconds"};d=function(d){var b,f;b=d-h;if(k!==c){switch(k){case "normal":0!==d&&g.push(["put",a.substring(h,d)]);break;case "Y":2===b?g.push(["getYear","getDblFig"]):4===b?g.push(["getFullYear"]):g.push(["put",a.substring(h,d)]);break;default:f=e[k],1===b?"M"===k?g.push([f,"plus"]):g.push([f]):2===b?"M"===k?g.push([f,"plus","fmtZero"]):g.push([f,"fmtZero"]):g.push(["put",a.substring(h,d)])}return h=d}};
4+c=null;b=m=0;for(l=a.length;0<=l?m<l:m>l;b=0<=l?++m:--m)c=a.charAt(b),"Y"===c||("M"===c||"D"===c||"h"===c||"m"===c||"s"===c)||(c="normal"),d(b),k=c;c="";d(a.length);return this.scenario=g};return b}();s=function(){function b(a,c){this.numUser=a;this._=c;this.counter=0;this.timer=new v(this,"execCheck",10);this.writer=c.tickerDomWriter;this.setting=c.setting}b.prototype.check=function(){var a,c,d,b,e,g,h;a=this._.tickerData;b=a.tweetList;e=a.tweetNum;c=a.failure;var k;h=0;for(g in c)(k=c[g])&&h++;
5+g=0;for(d in b)g++;if(g!==this.numUser-h)return!1;for(d in b)if(h=b[d],h.length!==e[d])return!1;for(d in c)a.failure[d]=!1;return!0};b.prototype.execCheck=function(){return this.check()?(this.writer.cleanup(),this.writer.generateList(null)):200>++this.counter?this.timer.restart(10):this.writer.generateList(this.setting.responseFailureMessage)};return b}();y=function(){function b(a,c,d){this.span=a;this.target=c;this._=d;this.interval=this._.setting.interval;this.movingQuantity=this._.setting.movingQuantity;
6+this.speeddown=0;this.prepare=!1;this.timer=new v(this,"move",this.interval)}b.prototype.move=function(){var a,c,d,b,e;b=parseInt(this.span.style.left);d=this.span.offsetWidth;0!==this.speeddown&&(this.speeddown+=this.speeddown);c=this.movingQuantity-this.speeddown;a=q(this._.setting.autoReload);if(0<c){b-=c;if(b<-d){b=this.target.style.width?parseInt(this.target.style.width):this.target.offsetWidth;if(this.span.prepare){e=this.span.prepare;c=0;for(d=e.length;c<d;c++)a=e[c],this.span.appendChild(a)}this.span.prepare=
7+!1}else a&&(!this.prepare&&b<-d+250)&&this.getTweetsInBackground();this.span.style.left=b+(this.span.measure||"px");return this.timer.restart(this.interval)}return this.timer.abort()};b.prototype.getTweetsInBackground=function(){var a,c,d,b,e,g;d=this._.tickerData.userList;b=this._.setting.numberOfRecord;this.prepare=!0;a=new n(this.target,this._);c=e=0;for(g=d.length;0<=g?e<=g:e>=g;c=0<=g?++e:--e)a.setCaller(d[c],b,a._numberCode(c));return new s(d.length)};return b}();z=function(){function b(a){this.userList=
8+this._makeUserListFrom(a);this.failure={};this.tweetList={};this.tweetNum={};this.dateFormatter=this.animation=null}b.prototype._makeUserListFrom=function(a){var c,b,f,e;b=[];a=a.users;if(!a||""===a)return[];a=a.substring(0,160).split(/,\s*/);try{f=0;for(e=a.length;f<e;f++)c=a[f],b.push(encodeURI(c))}catch(g){}return b};return b}();A=function(){function b(a,c){this.target=a;this.browser=c;this.target.style.overflow="hidden";this.currentTarget=this.target}b.prototype.addNoWrapElement=function(){var a,
9+c,b,f;b={whiteSpace:"pre",position:"relative",left:t.spanLeft(this.target)};this.browser.canAcceptIntrinsicWidth()?(a=document.createElement("div"),b.width="intrinsic"):a=document.createElement("span");for(c in b)(f=b[c])&&(a.style[c]=f);this.currentTarget.appendChild(a);return this.currentTarget=a};b.prototype.addTextNode=function(a){a=document.createTextNode(a);return this.currentTarget.appendChild(a)};return b}();t={measureOf:function(b){var a;a=/^[0-9\.]+\s*([a-zA-Z]+)$/;return(b=b.style.width)&&
10+b.match(a)?b.replace(a,"$1"):null},widthOf:function(b){var a;a=b.style;return a.width?parseInt(a.width):b.offsetWidth},spanLeft:function(b){var a,c;c=t;a=c.measureOf(b);return c.widthOf(b)+(a?a:"px")}};B=function(){function b(a,c){this.target=a;this._=c;this.setting=this._.setting;this.tickerData=this._.tickerData}b.prototype.cleanup=function(){var a,c,b,f,e,g;c=this.target.childNodes;g=[];f=0;for(e=c.length;f<e;f++)(b=(a=c[f])?a.tagName:null)&&("script"===b||"SCRIPT"===b)?g.push(this.target.removeChild(a)):
11+g.push(void 0);return g};b.prototype.generateList=function(a){var c,b,f,e;b=this._listNodes(a);a=this.target.childNodes[0];if(0<a.childNodes.length)return a.prepare=b;f=0;for(e=b.length;f<e;f++)c=b[f],a.appendChild(c);return this.tickerData.animation=new y(a,this.target,this._)};b.prototype._listNodes=function(a){var c,b;a?a=this._errorLine(a):(b=q(this.setting.displayUserID),c=q(this.setting.displayDateTime),a=this._unitedList(),a=0<a.length?this.makeupTweetLine(a,c,b):this._errorLine(this.setting.notFoundMessage));
12+return a};b.prototype._errorList=function(a){var c;c=document.createElement("span");c.setAttribute("class","tsubu-error");a=document.createTextNode(a);c.appendChild(a);return[c]};b.prototype._unitedList=function(){var a,c,b,f,e,g,h;c=[];h=this.tickerData.tweetList;for(a in h){f=h[a];e=0;for(g=f.length;e<g;e++)b=f[e],c.push(b)}c.sort(function(a,c){return c.id-a.id});return c};b.prototype._tweetSpan=function(a){var c;c=document.createElement("span");c.setAttribute("class","tsubu-tweet");c.setAttribute("lang",
13+a.lang);c.innerHTML=a.text;return c};b.prototype._datetimeSpan=function(a){var c;c=document.createElement("span");c.setAttribute("class","tsubu-datetime");a=document.createTextNode(this._toDateFmt(a.date));c.appendChild(a);return c};b.prototype._userSpan=function(a){var c;c=document.createElement("a");c.setAttribute("class","tsubu-user");c.setAttribute("href","http://twitter.com/"+a.user);c.setAttribute("target","_blank");a=document.createTextNode(a.user);c.appendChild(a);return c};b.prototype._intervalSpan=
14+function(){var a,c,b;c=document.createElement("span");c.setAttribute("class","tsubu-sep");a=[];for(b=0;4>=b;++b)a.push("&nbsp;");c.innerHTML=a.join("&diams;");return c};b.prototype.makeupTweetLine=function(a,c,b){var f,e,g,h;e=[];f=g=0;for(h=this.tickerData.tweetNum[a[0].user];0<=h?g<=h:g>=h;f=0<=h?++g:--g)e.push(this._tweetSpan(a[f])),(c||b)&&e.push(document.createTextNode(" (")),c&&e.push(this._datetimeSpan(a[f])),c&&b&&e.push(document.createTextNode(" ")),b&&e.push(this._userSpan(a[f])),(c||b)&&
15+e.push(document.createTextNode(")")),f!==this.tickerData.tweetNum[a[0].user]-1&&(e.push(this._intervalSpan()),e.push(document.createTextNode(" ")));return e};b.prototype._toDateFmt=function(a){this.tickerData.dateFormatter||(this.tickerData.dateFormatter=new x,this.tickerData.dateFormatter.compile(this.setting.dateFormat));return this.tickerData.dateFormatter.apply(a)};return b}();u=function(){function b(a){var c,b;a=this._mergeUserSetting(a);for(c in a)b=a[c],this[c]=b}b.prototype._mergeUserSetting=
16+function(a){var c,d;d=b.defaultSetting;for(c in d)a[c]=a[c]||d[c];return a};return b}();u.defaultSetting={users:null,numberOfRecord:5,interval:100,movingQuantity:7,dateFormat:"YYYY/MM/DD hh:mm",displayUserID:"on",displayDateTime:"on",autoReload:"off",notFoundMessage:"[INFO] NO TWEET WAS FOUND",responseFailureMessage:"[TIMEOUT] SOME RESPONSES WERE NOT RETURNED"};v=function(){function b(a,c,b){this.target=a;this.method=c;this.id=this.setTimer(b)}b.prototype.abort=function(){return clearTimeout(this.id)};
17+b.prototype.restart=function(a){return this.id=this.setTimer(a)};b.prototype.setTimer=function(a){var b,d;d=this.target;b=this.method;return setTimeout(function(){if(d[b]())return d[b]},a)};return b}();k=null;D=function(b,a){var c,d,f,e,g,h;c=k.tickerData;d=b.results;if(0<d.length){c.tweetList[d[0].from_user]=[];c.tweetNum[d[0].from_user]=d.length;c=c.tweetList[d[0].from_user];h=[];e=0;for(g=d.length;e<g;e++)f=d[e],h.push(c.push(new C(f.from_user,f.text,f.id,f.created_at,f.iso_language_code)));return h}return c.failure[a]=
18+!0};q=function(b){return"off"!==b.toLowerCase()};w=function(){var b,a;b=new r;a=document.getElementById("tsubuyakiTicker");(new A(a,b)).addNoWrapElement();k=window.TsubuyakiTicker;k.tickerDomWriter=new B(a,k);(new n(a,k)).collect();return new s(k.tickerData.userList.length,k)};window&&(window.TsubuyakiTicker=function(b){k=window.TsubuyakiTicker;k.tweets=D;k.callbacks={};k.setting=new u(b);return k.tickerData=new z(k.setting)},window.addEventListener?window.addEventListener("load",w,!1):window.attachEvent&&
19+window.attachEvent("onload",w));C=function(){function b(a,b,d,f,e){this.user=a;this.id=d;this.date=f;this.lang=e;this.text=this._linkedText(b)}b.prototype._linkedText=function(a){var b,d,f,e,g;d=[];f=p.REGEXPS;for(b in f)d.push(b);f=new p;e=0;for(g=d.length;e<g;e++)b=d[e],a=f[b](a);return a};return b}();p=function(){function b(){this.reg=b.REGEXPS}b.prototype.linkTag=function(a,b){return'<a href="'+a+'" target="_blank">'+b+"</a>"};b.prototype.link=function(a){return a.replace(this.reg.link,this.linkTag("$1",
20+"$1"))};b.prototype.account0=function(a){return a.replace(this.reg.account0,"$1"+this.linkTag("http://twitter.com/$2","$2"))};b.prototype.account1=function(a){return a.replace(this.reg.account1,this.linkTag("http://twitter.com/$1","$1"))};b.prototype.hash0=function(a){return a.replace(this.reg.hash0,"$1"+this.linkTag("http://twitter.com/search?q=$2","$2"))};b.prototype.hash1=function(a){return a.replace(this.reg.hash1,this.linkTag("http://twitter.com/search?q=$1","$1"))};return b}();p.REGEXPS={link:/(http:\/\/[0-9A-Za-z\.\/_\?&\~\-\=\#\:%]+)/g,
21+account0:/(\s|[^a-zA-Z0-9])(@[a-zA-Z0-9_]+)/g,account1:/^(@[a-zA-Z0-9_]+)/,hash0:/(\s)(\#[a-zA-Z0-9_]+)/g,hash1:/^(\#[a-zA-Z0-9_]+)/};n=function(){function b(a,b){this.target=a;this._=b}b.prototype.collect=function(){var a,b,d,f,e;a=-1;b=this._.setting.numberOfRecord;f=this._.tickerData.userList;e=[];d=0;for(f=f.length;d<f;d++)e.push(this.setCallerAndCallback(++a,b));return e};b.prototype.setCallerAndCallback=function(a,b){var d,f;d=this._numberCode(a);f=this._;this._.tickerData.failure[d]=!1;this._.callbacks[d]=
22+function(){return function(a){return f.tweets(a,d)}}();return this.setCaller(this._.tickerData.userList[a],b,d)};b.prototype.setCaller=function(a,b,d){var f;f=document.createElement("script");f.type="text/javascript";f.src=this._makeupAPIURL({query:"from%3A"+a,numberOfRecord:b,callback:"TsubuyakiTicker.callbacks."+d});return this.target.appendChild(f)};b.prototype._makeupAPIURL=function(a){var c;c=b.setting;a=this._queryString(a,c.paramKeys);return""+c.host+c.restAPI+"?"+a};b.prototype._queryString=
23+function(a,b){var d,f,e;d=[];for(f in a)e=a[f],d.push(""+b[f]+"="+e);return d.join("&")};b.prototype._numberCode=function(a){var b,d;for(b=[];!(d=a%10,a=(a-d)/10,b.push(String.fromCharCode(d+97)),0>=a););return b.join("")};return b}();n.setting={host:"http://search.twitter.com/",restAPI:"search.json",paramKeys:{query:"q",numberOfRecord:"rpp",callback:"callback"}}}).call(this);
--- tsubuyakiticker/js/tsubuyakiTicker.js (nonexistent)
+++ tsubuyakiticker/js/tsubuyakiTicker.js (revision 15)
@@ -0,0 +1,897 @@
1+// Generated by CoffeeScript 1.6.2
2+(function() {
3+ var Browser, DateFormatter, ListManager, TickerAnimation, TickerData, TickerDomBuilder, TickerDomUtil, TickerDomWriter, TickerSetting, TickerTimer, Tweet, TweetCollector, TweetTextReplacer, checkSwitch, run, tweets, _pub;
4+
5+ Browser = (function() {
6+ function Browser() {
7+ if (window) {
8+ this.userAgent = window.navigator.userAgent;
9+ }
10+ this.names = Browser.names;
11+ }
12+
13+ Browser.prototype.name = function() {
14+ var nm, ua, _i, _len, _ref;
15+
16+ if (!this.userAgent) {
17+ return 'other';
18+ }
19+ ua = this.userAgent.toLowerCase();
20+ _ref = this.names;
21+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
22+ nm = _ref[_i];
23+ if ((ua.indexOf(nm)) !== -1) {
24+ return nm;
25+ }
26+ }
27+ return 'other';
28+ };
29+
30+ Browser.prototype.canAcceptIntrinsicWidth = function() {
31+ return this.name() === 'chrome';
32+ };
33+
34+ return Browser;
35+
36+ })();
37+
38+ Browser.names = ['ie', 'chrome', 'safari', 'gecko', 'opera'];
39+
40+ DateFormatter = (function() {
41+ function DateFormatter() {
42+ this.scenario = [];
43+ }
44+
45+ DateFormatter.prototype.apply = function(text) {
46+ var buf, d, j, r, s, _i, _j, _len, _ref, _ref1;
47+
48+ r = [];
49+ d = new Date(text);
50+ _ref = this.scenario;
51+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
52+ s = _ref[_i];
53+ if (s[0] === 'put') {
54+ r.push(s[1]);
55+ } else {
56+ if (s[0]) {
57+ buf = d[s[0]]();
58+ }
59+ for (j = _j = 1, _ref1 = s.length; 1 <= _ref1 ? _j < _ref1 : _j > _ref1; j = 1 <= _ref1 ? ++_j : --_j) {
60+ if (s[j]) {
61+ buf = this[s[j]](buf);
62+ }
63+ }
64+ r.push(buf);
65+ }
66+ }
67+ return r.join('');
68+ };
69+
70+ DateFormatter.prototype.plus = function(value) {
71+ return (parseInt(value)) + 1;
72+ };
73+
74+ DateFormatter.prototype.fmtZero = function(value) {
75+ if ((String(value)).length === 1) {
76+ return "0" + value;
77+ } else {
78+ return value;
79+ }
80+ };
81+
82+ DateFormatter.prototype.getDblFig = function(value) {
83+ var s;
84+
85+ s = String(value);
86+ return s.substring(s.length - 2);
87+ };
88+
89+ DateFormatter.prototype.compile = function(format) {
90+ var c, changeStatus, i, map, p, start, status, _i, _ref;
91+
92+ p = [];
93+ status = 'normal';
94+ start = 0;
95+ map = {
96+ 'M': 'getMonth',
97+ 'D': 'getDate',
98+ 'h': 'getHours',
99+ 'm': 'getMinutes',
100+ 's': 'getSeconds'
101+ };
102+ changeStatus = function(i) {
103+ var len, m;
104+
105+ len = i - start;
106+ if (status !== c) {
107+ switch (status) {
108+ case 'normal':
109+ if (i !== 0) {
110+ p.push(['put', format.substring(start, i)]);
111+ }
112+ break;
113+ case 'Y':
114+ if (len === 2) {
115+ p.push(['getYear', 'getDblFig']);
116+ } else if (len === 4) {
117+ p.push(['getFullYear']);
118+ } else {
119+ p.push(['put', format.substring(start, i)]);
120+ }
121+ break;
122+ default:
123+ m = map[status];
124+ if (len === 1) {
125+ if (status === 'M') {
126+ p.push([m, 'plus']);
127+ } else {
128+ p.push([m]);
129+ }
130+ } else if (len === 2) {
131+ if (status === 'M') {
132+ p.push([m, 'plus', 'fmtZero']);
133+ } else {
134+ p.push([m, 'fmtZero']);
135+ }
136+ } else {
137+ p.push(['put', format.substring(start, i)]);
138+ }
139+ }
140+ return start = i;
141+ }
142+ };
143+ c = null;
144+ for (i = _i = 0, _ref = format.length; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) {
145+ c = format.charAt(i);
146+ if (!(c === 'Y' || c === 'M' || c === 'D' || c === 'h' || c === 'm' || c === 's')) {
147+ c = 'normal';
148+ }
149+ changeStatus(i);
150+ status = c;
151+ }
152+ c = '';
153+ changeStatus(format.length);
154+ return this.scenario = p;
155+ };
156+
157+ return DateFormatter;
158+
159+ })();
160+
161+ ListManager = (function() {
162+ function ListManager(numUser, _public) {
163+ this.numUser = numUser;
164+ this._ = _public;
165+ this.counter = 0;
166+ this.timer = new TickerTimer(this, 'execCheck', 10);
167+ this.writer = _public.tickerDomWriter;
168+ this.setting = _public.setting;
169+ }
170+
171+ ListManager.prototype.check = function() {
172+ var data, failure, failureCount, i, k, list, num, size, t;
173+
174+ data = this._.tickerData;
175+ list = data.tweetList;
176+ num = data.tweetNum;
177+ failure = data.failure;
178+ failureCount = (function() {
179+ var k, r, v;
180+
181+ r = 0;
182+ for (k in failure) {
183+ v = failure[k];
184+ if (v) {
185+ r++;
186+ }
187+ }
188+ return r;
189+ })();
190+ size = 0;
191+ for (k in list) {
192+ t = list[k];
193+ size++;
194+ }
195+ if (size !== this.numUser - failureCount) {
196+ return false;
197+ }
198+ i =  0;
199+ for (k in list) {
200+ t = list[k];
201+ if (t.length !== num[k]) {
202+ return false;
203+ }
204+ }
205+ for (k in failure) {
206+ t = failure[k];
207+ data.failure[k] = false;
208+ }
209+ return true;
210+ };
211+
212+ ListManager.prototype.execCheck = function() {
213+ if (!this.check()) {
214+ if (++this.counter < 200) {
215+ return this.timer.restart(10);
216+ } else {
217+ return this.writer.generateList(this.setting.responseFailureMessage);
218+ }
219+ } else {
220+ this.writer.cleanup();
221+ return this.writer.generateList(null);
222+ }
223+ };
224+
225+ return ListManager;
226+
227+ })();
228+
229+ TickerAnimation = (function() {
230+ function TickerAnimation(span, target, _) {
231+ this.span = span;
232+ this.target = target;
233+ this._ = _;
234+ this.interval = this._.setting.interval;
235+ this.movingQuantity = this._.setting.movingQuantity;
236+ this.speeddown = 0;
237+ this.prepare = false;
238+ this.timer = new TickerTimer(this, 'move', this.interval);
239+ }
240+
241+ TickerAnimation.prototype.move = function() {
242+ var auto, node, v, w, x, _i, _len, _ref;
243+
244+ x = parseInt(this.span.style.left);
245+ w = this.span.offsetWidth;
246+ if (this.speeddown !== 0) {
247+ this.speeddown += this.speeddown;
248+ }
249+ v = this.movingQuantity - this.speeddown;
250+ auto = checkSwitch(this._.setting.autoReload);
251+ if (v > 0) {
252+ x -= v;
253+ if (x < -w) {
254+ x = this.target.style.width ? parseInt(this.target.style.width) : this.target.offsetWidth;
255+ if (this.span.prepare) {
256+ _ref = this.span.prepare;
257+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
258+ node = _ref[_i];
259+ this.span.appendChild(node);
260+ }
261+ }
262+ this.span.prepare = false;
263+ } else if (auto && !this.prepare && x < -w + 250) {
264+ this.getTweetsInBackground();
265+ }
266+ this.span.style.left = x + (this.span.measure || 'px');
267+ return this.timer.restart(this.interval);
268+ } else {
269+ return this.timer.abort();
270+ }
271+ };
272+
273+ TickerAnimation.prototype.getTweetsInBackground = function() {
274+ var c, i, l, n, _i, _ref;
275+
276+ l = this._.tickerData.userList;
277+ n = this._.setting.numberOfRecord;
278+ this.prepare = true;
279+ c = new TweetCollector(this.target, this._);
280+ for (i = _i = 0, _ref = l.length; 0 <= _ref ? _i <= _ref : _i >= _ref; i = 0 <= _ref ? ++_i : --_i) {
281+ c.setCaller(l[i], n, c._numberCode(i));
282+ }
283+ return new ListManager(l.length);
284+ };
285+
286+ return TickerAnimation;
287+
288+ })();
289+
290+ TickerData = (function() {
291+ function TickerData(setting) {
292+ this.userList = this._makeUserListFrom(setting);
293+ this.failure = {};
294+ this.tweetList = {};
295+ this.tweetNum = {};
296+ this.animation = null;
297+ this.dateFormatter = null;
298+ }
299+
300+ TickerData.prototype._makeUserListFrom = function(setting) {
301+ var e, it, items, list, s, _i, _len;
302+
303+ list = [];
304+ s = setting.users;
305+ if (!s || s === '') {
306+ return [];
307+ }
308+ items = (s.substring(0, 160)).split(/,\s*/);
309+ try {
310+ for (_i = 0, _len = items.length; _i < _len; _i++) {
311+ it = items[_i];
312+ list.push(encodeURI(it));
313+ }
314+ } catch (_error) {
315+ e = _error;
316+ }
317+ return list;
318+ };
319+
320+ return TickerData;
321+
322+ })();
323+
324+ /*
325+ TickerDomBuilder
326+ --------------------------------------------------
327+ A class to make up a DIV element for the tsubuyaki ticker.
328+
329+ [usage]
330+ STEP 1. get an instance.
331+ b = new TickerDomBuilder element, browser
332+
333+ STEP 2. add a span with the no-wrap style
334+ b.addNoWrapElement()
335+ */
336+
337+
338+ TickerDomBuilder = (function() {
339+ function TickerDomBuilder(target, browser) {
340+ this.target = target;
341+ this.browser = browser;
342+ this.target.style.overflow = 'hidden';
343+ this.currentTarget = this.target;
344+ }
345+
346+ TickerDomBuilder.prototype.addNoWrapElement = function() {
347+ var elem, k, style, util, v;
348+
349+ util = TickerDomUtil;
350+ style = {
351+ whiteSpace: 'pre',
352+ position: 'relative',
353+ left: util.spanLeft(this.target)
354+ };
355+ elem = null;
356+ if (this.browser.canAcceptIntrinsicWidth()) {
357+ elem = document.createElement('div');
358+ style['width'] = 'intrinsic';
359+ } else {
360+ elem = document.createElement('span');
361+ }
362+ for (k in style) {
363+ v = style[k];
364+ if (v) {
365+ elem.style[k] = v;
366+ }
367+ }
368+ this.currentTarget.appendChild(elem);
369+ return this.currentTarget = elem;
370+ };
371+
372+ TickerDomBuilder.prototype.addTextNode = function(text) {
373+ var t;
374+
375+ t = document.createTextNode(text);
376+ return this.currentTarget.appendChild(t);
377+ };
378+
379+ return TickerDomBuilder;
380+
381+ })();
382+
383+ TickerDomUtil = {
384+ measureOf: function(element) {
385+ var reg, w;
386+
387+ reg = /^[0-9\.]+\s*([a-zA-Z]+)$/;
388+ w = element.style.width;
389+ if (w && w.match(reg)) {
390+ return w.replace(reg, '$1');
391+ } else {
392+ return null;
393+ }
394+ },
395+ widthOf: function(element) {
396+ var s;
397+
398+ s = element.style;
399+ if (s.width) {
400+ return parseInt(s.width);
401+ } else {
402+ return element.offsetWidth;
403+ }
404+ },
405+ spanLeft: function(element) {
406+ var m, u, w;
407+
408+ u = TickerDomUtil;
409+ m = u.measureOf(element);
410+ w = u.widthOf(element);
411+ return w + (m ? m : 'px');
412+ }
413+ };
414+
415+ TickerDomWriter = (function() {
416+ function TickerDomWriter(target, _public) {
417+ this.target = target;
418+ this._ = _public;
419+ this.setting = this._.setting;
420+ this.tickerData = this._.tickerData;
421+ }
422+
423+ TickerDomWriter.prototype.cleanup = function() {
424+ var nd, nds, nm, _i, _len, _results;
425+
426+ nds = this.target.childNodes;
427+ _results = [];
428+ for (_i = 0, _len = nds.length; _i < _len; _i++) {
429+ nd = nds[_i];
430+ nm = nd ? nd.tagName : null;
431+ if (nm && (nm === 'script' || nm === 'SCRIPT')) {
432+ _results.push(this.target.removeChild(nd));
433+ } else {
434+ _results.push(void 0);
435+ }
436+ }
437+ return _results;
438+ };
439+
440+ TickerDomWriter.prototype.generateList = function(errorMessage) {
441+ var d, node, nodes, _i, _len;
442+
443+ nodes = this._listNodes(errorMessage);
444+ d = this.target.childNodes[0];
445+ if (d.childNodes.length > 0) {
446+ return d.prepare = nodes;
447+ } else {
448+ for (_i = 0, _len = nodes.length; _i < _len; _i++) {
449+ node = nodes[_i];
450+ d.appendChild(node);
451+ }
452+ return this.tickerData.animation = new TickerAnimation(d, this.target, this._);
453+ }
454+ };
455+
456+ TickerDomWriter.prototype._listNodes = function(errorMessage) {
457+ var list, nodes, showDate, showUser;
458+
459+ nodes = null;
460+ if (errorMessage) {
461+ nodes = this._errorLine(errorMessage);
462+ } else {
463+ showUser = checkSwitch(this.setting.displayUserID);
464+ showDate = checkSwitch(this.setting.displayDateTime);
465+ list = this._unitedList();
466+ if (list.length > 0) {
467+ nodes = this.makeupTweetLine(list, showDate, showUser);
468+ } else {
469+ nodes = this._errorLine(this.setting.notFoundMessage);
470+ }
471+ }
472+ return nodes;
473+ };
474+
475+ TickerDomWriter.prototype._errorList = function(errorMessage) {
476+ var span, text;
477+
478+ span = document.createElement('span');
479+ span.setAttribute("class", "tsubu-error");
480+ text = document.createTextNode(errorMessage);
481+ span.appendChild(text);
482+ return [span];
483+ };
484+
485+ TickerDomWriter.prototype._unitedList = function() {
486+ var k, l, t, v, _i, _len, _ref;
487+
488+ l = [];
489+ _ref = this.tickerData.tweetList;
490+ for (k in _ref) {
491+ v = _ref[k];
492+ for (_i = 0, _len = v.length; _i < _len; _i++) {
493+ t = v[_i];
494+ l.push(t);
495+ }
496+ }
497+ l.sort(function(a, b) {
498+ return b.id - a.id;
499+ });
500+ return l;
501+ };
502+
503+ TickerDomWriter.prototype._tweetSpan = function(list) {
504+ var span;
505+
506+ span = document.createElement('span');
507+ span.setAttribute('class', 'tsubu-tweet');
508+ span.setAttribute('lang', list.lang);
509+ span.innerHTML = list.text;
510+ return span;
511+ };
512+
513+ TickerDomWriter.prototype._datetimeSpan = function(list) {
514+ var span, text;
515+
516+ span = document.createElement('span');
517+ span.setAttribute('class', 'tsubu-datetime');
518+ text = document.createTextNode(this._toDateFmt(list.date));
519+ span.appendChild(text);
520+ return span;
521+ };
522+
523+ TickerDomWriter.prototype._userSpan = function(list) {
524+ var span, text;
525+
526+ span = document.createElement('a');
527+ span.setAttribute('class', 'tsubu-user');
528+ span.setAttribute('href', "http://twitter.com/" + list.user);
529+ span.setAttribute('target', '_blank');
530+ text = document.createTextNode(list.user);
531+ span.appendChild(text);
532+ return span;
533+ };
534+
535+ TickerDomWriter.prototype._intervalSpan = function() {
536+ var diams, i, span, _i;
537+
538+ span = document.createElement('span');
539+ span.setAttribute('class', 'tsubu-sep');
540+ diams = [];
541+ for (i = _i = 0; _i <= 4; i = ++_i) {
542+ diams.push('&nbsp;');
543+ }
544+ span.innerHTML = diams.join('&diams;');
545+ return span;
546+ };
547+
548+ TickerDomWriter.prototype.makeupTweetLine = function(list, showDatetime, showUser) {
549+ var n, nodes, _i, _ref;
550+
551+ nodes = [];
552+ for (n = _i = 0, _ref = this.tickerData.tweetNum[list[0].user]; 0 <= _ref ? _i <= _ref : _i >= _ref; n = 0 <= _ref ? ++_i : --_i) {
553+ nodes.push(this._tweetSpan(list[n]));
554+ if (showDatetime || showUser) {
555+ nodes.push(document.createTextNode(' ('));
556+ }
557+ if (showDatetime) {
558+ nodes.push(this._datetimeSpan(list[n]));
559+ }
560+ if (showDatetime && showUser) {
561+ nodes.push(document.createTextNode(' '));
562+ }
563+ if (showUser) {
564+ nodes.push(this._userSpan(list[n]));
565+ }
566+ if (showDatetime || showUser) {
567+ nodes.push(document.createTextNode(')'));
568+ }
569+ if (n !== this.tickerData.tweetNum[list[0].user] - 1) {
570+ nodes.push(this._intervalSpan());
571+ nodes.push(document.createTextNode(' '));
572+ }
573+ }
574+ return nodes;
575+ };
576+
577+ TickerDomWriter.prototype._toDateFmt = function(text) {
578+ if (!this.tickerData.dateFormatter) {
579+ this.tickerData.dateFormatter = new DateFormatter();
580+ this.tickerData.dateFormatter.compile(this.setting.dateFormat);
581+ }
582+ return this.tickerData.dateFormatter.apply(text);
583+ };
584+
585+ return TickerDomWriter;
586+
587+ })();
588+
589+ TickerSetting = (function() {
590+ function TickerSetting(userSetting) {
591+ var k, s, v;
592+
593+ s = this._mergeUserSetting(userSetting);
594+ for (k in s) {
595+ v = s[k];
596+ this[k] = v;
597+ }
598+ }
599+
600+ TickerSetting.prototype._mergeUserSetting = function(setting) {
601+ var k, s, v;
602+
603+ s = TickerSetting.defaultSetting;
604+ for (k in s) {
605+ v = s[k];
606+ setting[k] = setting[k] || s[k];
607+ }
608+ return setting;
609+ };
610+
611+ return TickerSetting;
612+
613+ })();
614+
615+ TickerSetting.defaultSetting = {
616+ users: null,
617+ numberOfRecord: 5,
618+ interval: 100,
619+ movingQuantity: 7,
620+ dateFormat: 'YYYY/MM/DD hh:mm',
621+ displayUserID: 'on',
622+ displayDateTime: 'on',
623+ autoReload: 'off',
624+ notFoundMessage: '[INFO] NO TWEET WAS FOUND',
625+ responseFailureMessage: '[TIMEOUT] SOME RESPONSES WERE NOT RETURNED'
626+ };
627+
628+ TickerTimer = (function() {
629+ function TickerTimer(target, method, timeout) {
630+ this.target = target;
631+ this.method = method;
632+ this.id = this.setTimer(timeout);
633+ }
634+
635+ TickerTimer.prototype.abort = function() {
636+ return clearTimeout(this.id);
637+ };
638+
639+ TickerTimer.prototype.restart = function(timeout) {
640+ return this.id = this.setTimer(timeout);
641+ };
642+
643+ TickerTimer.prototype.setTimer = function(timeout) {
644+ var f, m, t;
645+
646+ t = this.target;
647+ m = this.method;
648+ f = function() {
649+ if (t[m]()) {
650+ return t[m];
651+ }
652+ };
653+ return setTimeout(f, timeout);
654+ };
655+
656+ return TickerTimer;
657+
658+ })();
659+
660+ _pub = null;
661+
662+ tweets = function(result, code) {
663+ var d, l, r, rec, _i, _len, _results;
664+
665+ d = _pub.tickerData;
666+ r = result.results;
667+ if (r.length > 0) {
668+ d.tweetList[r[0].from_user] = [];
669+ d.tweetNum[r[0].from_user] = r.length;
670+ l = d.tweetList[r[0].from_user];
671+ _results = [];
672+ for (_i = 0, _len = r.length; _i < _len; _i++) {
673+ rec = r[_i];
674+ _results.push(l.push(new Tweet(rec.from_user, rec.text, rec.id, rec.created_at, rec.iso_language_code)));
675+ }
676+ return _results;
677+ } else {
678+ return d.failure[code] = true;
679+ }
680+ };
681+
682+ checkSwitch = function(value) {
683+ return value.toLowerCase() !== 'off';
684+ };
685+
686+ run = function() {
687+ var b, browser, c, target;
688+
689+ browser = new Browser();
690+ target = document.getElementById('tsubuyakiTicker');
691+ b = new TickerDomBuilder(target, browser);
692+ b.addNoWrapElement();
693+ _pub = window.TsubuyakiTicker;
694+ _pub.tickerDomWriter = new TickerDomWriter(target, _pub);
695+ c = new TweetCollector(target, _pub);
696+ c.collect();
697+ return new ListManager(_pub.tickerData.userList.length, _pub);
698+ };
699+
700+ if (window) {
701+ window.TsubuyakiTicker = function(setting) {
702+ _pub = window.TsubuyakiTicker;
703+ _pub.tweets =  tweets;
704+ _pub.callbacks = {};
705+ _pub.setting = new TickerSetting(setting);
706+ return _pub.tickerData = new TickerData(_pub.setting);
707+ };
708+ if (window.addEventListener) {
709+ window.addEventListener('load', run, false);
710+ } else if (window.attachEvent) {
711+ window.attachEvent('onload', run);
712+ }
713+ }
714+
715+ Tweet = (function() {
716+ function Tweet(user, text, id, date, lang) {
717+ this.user = user;
718+ this.id = id;
719+ this.date = date;
720+ this.lang = lang;
721+ this.text = this._linkedText(text);
722+ }
723+
724+ Tweet.prototype._linkedText = function(text) {
725+ var k, keys, replacer, v, _i, _len, _ref;
726+
727+ keys = [];
728+ _ref = TweetTextReplacer.REGEXPS;
729+ for (k in _ref) {
730+ v = _ref[k];
731+ keys.push(k);
732+ }
733+ replacer = new TweetTextReplacer;
734+ for (_i = 0, _len = keys.length; _i < _len; _i++) {
735+ k = keys[_i];
736+ text = replacer[k](text);
737+ }
738+ return text;
739+ };
740+
741+ return Tweet;
742+
743+ })();
744+
745+ TweetTextReplacer = (function() {
746+ function TweetTextReplacer() {
747+ this.reg = TweetTextReplacer.REGEXPS;
748+ }
749+
750+ TweetTextReplacer.prototype.linkTag = function(href, content) {
751+ return "<a href=\"" + href + "\" target=\"_blank\">" + content + "</a>";
752+ };
753+
754+ TweetTextReplacer.prototype.link = function(text) {
755+ return text.replace(this.reg['link'], this.linkTag('$1', '$1'));
756+ };
757+
758+ TweetTextReplacer.prototype.account0 = function(text) {
759+ var href;
760+
761+ href = "http://twitter.com/$2";
762+ return text.replace(this.reg['account0'], "$1" + (this.linkTag(href, '$2')));
763+ };
764+
765+ TweetTextReplacer.prototype.account1 = function(text) {
766+ var href;
767+
768+ href = "http://twitter.com/$1";
769+ return text.replace(this.reg['account1'], this.linkTag(href, '$1'));
770+ };
771+
772+ TweetTextReplacer.prototype.hash0 = function(text) {
773+ var href;
774+
775+ href = "http://twitter.com/search?q=$2";
776+ return text.replace(this.reg['hash0'], "$1" + (this.linkTag(href, '$2')));
777+ };
778+
779+ TweetTextReplacer.prototype.hash1 = function(text) {
780+ var href;
781+
782+ href = "http://twitter.com/search?q=$1";
783+ return text.replace(this.reg['hash1'], this.linkTag(href, '$1'));
784+ };
785+
786+ return TweetTextReplacer;
787+
788+ })();
789+
790+ TweetTextReplacer.REGEXPS = {
791+ link: /(http:\/\/[0-9A-Za-z\.\/_\?&\~\-\=\#\:%]+)/g,
792+ account0: /(\s|[^a-zA-Z0-9])(@[a-zA-Z0-9_]+)/g,
793+ account1: /^(@[a-zA-Z0-9_]+)/,
794+ hash0: /(\s)(\#[a-zA-Z0-9_]+)/g,
795+ hash1: /^(\#[a-zA-Z0-9_]+)/
796+ };
797+
798+ TweetCollector = (function() {
799+ function TweetCollector(target, _) {
800+ this.target = target;
801+ this._ = _;
802+ }
803+
804+ TweetCollector.prototype.collect = function() {
805+ var i, num, user, _i, _len, _ref, _results;
806+
807+ i = -1;
808+ num = this._.setting.numberOfRecord;
809+ _ref = this._.tickerData.userList;
810+ _results = [];
811+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
812+ user = _ref[_i];
813+ _results.push(this.setCallerAndCallback(++i, num));
814+ }
815+ return _results;
816+ };
817+
818+ TweetCollector.prototype.setCallerAndCallback = function(index, numberOfRecord) {
819+ var c, _;
820+
821+ c = this._numberCode(index);
822+ _ = this._;
823+ this._.tickerData.failure[c] = false;
824+ this._.callbacks[c] = (function() {
825+ var p;
826+
827+ p = c;
828+ return function(r) {
829+ return _.tweets(r, p);
830+ };
831+ })();
832+ return this.setCaller(this._.tickerData.userList[index], numberOfRecord, c);
833+ };
834+
835+ TweetCollector.prototype.setCaller = function(account, numOfRec, numCode) {
836+ var s;
837+
838+ s = document.createElement('script');
839+ s.type = 'text/javascript';
840+ s.src = this._makeupAPIURL({
841+ query: "from%3A" + account,
842+ numberOfRecord: numOfRec,
843+ callback: "TsubuyakiTicker.callbacks." + numCode
844+ });
845+ return this.target.appendChild(s);
846+ };
847+
848+ TweetCollector.prototype._makeupAPIURL = function(paramSetting) {
849+ var params, s;
850+
851+ s = TweetCollector.setting;
852+ params = this._queryString(paramSetting, s.paramKeys);
853+ return "" + s.host + s.restAPI + "?" + params;
854+ };
855+
856+ TweetCollector.prototype._queryString = function(paramSetting, keys) {
857+ var items, k, v;
858+
859+ items = [];
860+ for (k in paramSetting) {
861+ v = paramSetting[k];
862+ items.push("" + keys[k] + "=" + v);
863+ }
864+ return items.join('&');
865+ };
866+
867+ TweetCollector.prototype._numberCode = function(num) {
868+ var n, s, v;
869+
870+ n = num;
871+ s = [];
872+ while (true) {
873+ v = n % 10;
874+ n = (n - v) / 10;
875+ s.push(String.fromCharCode(v + 0x61));
876+ if (n <= 0) {
877+ break;
878+ }
879+ }
880+ return s.join('');
881+ };
882+
883+ return TweetCollector;
884+
885+ })();
886+
887+ TweetCollector.setting = {
888+ host: "http://search.twitter.com/",
889+ restAPI: "search.json",
890+ paramKeys: {
891+ query: "q",
892+ numberOfRecord: "rpp",
893+ callback: "callback"
894+ }
895+ };
896+
897+}).call(this);
旧リポジトリブラウザで表示