rewrite all with coffee-script
@@ -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(' ('); | |
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(' '); } | |
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"> ♦ ♦ ♦ </span> '); | |
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 | -} |
@@ -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> |
@@ -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 |
@@ -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 | + ] |
@@ -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 ' ' for i in [0..4] | |
88 | + span.innerHTML = diams.join '♦' | |
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 |
@@ -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' |
@@ -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" |
@@ -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 |
@@ -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 |
@@ -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 | + |
@@ -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 |
@@ -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 |
@@ -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') |
@@ -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_]+)/ |
@@ -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(" ");c.innerHTML=a.join("♦");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); |
@@ -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(' '); | |
543 | + } | |
544 | + span.innerHTML = diams.join('♦'); | |
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); |