There are 11 previous versions of this script.
Add Syntax Highlighting (this will take a few seconds, probably freezing your browser while it works)
// ==UserScript==
// @name oflotter
// @description (ver.1.8) Twitterにあれこれ機能追加するよ!
// @version 1.8
// @author oflow
// @namespace http://oflow.me/
// @include http://twitter.com/*
// @include https://twitter.com/*
// ==/UserScript==
//
//
// Ver.1.8
// ow.ly, imgur, plixi(Tweetphoto), Mobypicture, pic.im, Twitgoo, img.ly のサムネ追加
//
// Ver.1.7.5
// since_idの取得失敗してたの修正
//
// Ver.1.7.4
// タイマー増やしすぎたので減らした
//
// Ver.1.7.3
// Retweetされたやつに付いてくるアレ消した
//
// Ver.1.7.2
// bufferedなあれクリアしてなかった!
// home画面なんかで重複して取得してたの修正
//
// Ver.1.7.1
// にょきにょきさらに仕様変更(li:animateedがあったら順番にする)
// 短縮URLあたりの挙動変更
//
// Ver.1.7
// にょきにょきの仕様変更
// 自動更新みたいなの
//
// Ver.1.6.4
// flic.krのサムネ取得
// www.flickr.comのURL貼られてるの見てないから短縮URLだけ
// oflow.infoドメインそろそろ使えるやろ
//
// Ver.1.6.3
// URL置換間違ってた
//
// Ver.1.6.2
// 新UIになんかしねーから!のやつ修正
//
// Ver.1.6.1
// favstarの仕様変わってたからYahoo! Pipesも変更した。(RSS→JSONの方が楽な気がしてきた)
//
// Ver.1.6
// 新しいバージョンあるかチェックする
// "id_str" と "in_reply_to_status_id_str" 使うようにした
// in_reply_to取得時にエラーだったら何か出るかもしれない
// t.coとhtn.toと(略)のURL展開に対応するためにあれこれ誤魔化した
// 短縮URLは先読みしとくように変更(サムネだけ後回し)
// Instagramのサムネ表示
// body#showのとき背景色おかしかった
// ニコニコ動画のサムネのやつ修正
// Youtubeのサムネ表示
//
// Ver.1.5.2
// 5秒したら上のアレ消す
// 新UIの時は何もしない… はず
// buzztterは日本語でおk
// URL置換かえた
//
// Ver.1.5.1
// トレンドのん変更したときの動作修正
//
// Ver.1.5
// favstarのrecent fav取得
// buzztter関連の動作変更(buzztter以外も選択可能、buzztterの時だけ自動更新)
//
// Ver.1.4
// tinyurl展開に対応でその付近も変更
// amzn.to展開の時は商品画像出るかもしれない
// 4sq.com展開でアレしたけど遅くてダメかもしれない
// ホーム開いてる時トレンドをbuzztterに置き換えた
// ハッシュタグ置換おかしいのが若干マシになったかも
//
// Ver.1.3.1
// 新しいツイートが○○件ありますを押したとき遅いの改善(したはず)
// リプライ数表示リセットのタイミング変更
// 右側に画像サムネあるときのCSS調整
//
// Ver.1.3
// Firefox専用として作ってたoflotter jQuery版がChromeでも動かせるようになったんで前のはポイッと
// bit.ly URL展開がChrome対応
// URL展開した後にサムネ表示
// 英語版Twitter WebのをCSSを参考にそれっぽく調整
// 新しいツイートにスクロールの速度上げた気がする
// リプライ数表示のはそのツイートの本文っぽいところクリックすると「読んだ」扱いになる(はず)
// CSS調整
//
// Ver.1.2.2
// 新しいツイートにスクロールが埋まってたんで必要なさそうなページで非表示
//
// Ver.1.2.1
// 新しいツイートにスクロールの位置調整
// Replyリンク押したときの動作なおってなかったので修正
// 色分けしてたのがはみ出てたので修正
//
// Ver.1.2
// 英語表記にやや適当に対応
// 自分宛・自分が発言したツイートは色分け出来るようにした
// bit.lyのURLを展開 (bit.ly Proの短縮URL: 4sq.com, amzn.to, yhoo.itも展開するはず)
// 未読っぽいリプライ数をいい加減に表示するかも (status.bufferedで自分宛のがあったのをカウント)
// 新しいツイートが○○件をクリックしたらその辺にスクロール(かなり手抜き)
// 右上ナビのリスト取得あたりを修正
// (自分がフォローしてるリストも取得、自分以外のプロフィールを見てるときはその人のリストを取得)
// DM、Replyリンク押した時の動作修正
// CSS微調整
//
// ver.1.1
// ふぁぼれるようにボタン追加
// 自分にReply、DMボタンはさみしすぎるだろjk
// 右上のナビゲーションにリプライ、リストを追加
// (ホーム画面だとリストへアクセスしやすいかもしれん)
// いろいろ胡散臭い
//
// ver.1.0.4
// in_reply_toたどってたのにもreplyボタンとか付けた
// 読み込み中のときは何かぐるぐる回るようになった
// 画像がアレするのにyfrogと(jpg|png|gif)追加
// replyボタンでin_reply_status_id取得出来てないの修正
// replyボタン押したときの怪しげな動作は察して下さい
// templateの{screen_name}らへん間違ってた!
//
// ver.1.0.3
// Google Chrome対応(たぶん)
// 画像がアレするのはぱくりではないよ!
//
// ver.1.0.2
// 重い!ってことで「もっと読む」方式にした
// CSS微調整
//
// ver.1.0.1
// list, reply画面で動いてなかったの修正
// in_reply_toあったのに取得しないの修正
// CSS微調整
//
var oflotter_jquery = function() {
var version = '1.8';
// 背景色の設定
var color = {
replyToMe : '#faebd7', // 自分宛ツイートの背景色
mine : '#f0f8ff', // 自分が発言したツイートの背景色
inReplyTo : '#f3f3f3' // 取得した関連ツイートの背景色
};
var styles = [
'.retweet-ctx-dlg { display: none !important; }',
'#oflotter-buffered { display: none; opacity: 0; }',
'.status.in-queue { display: none; opacity: 0; }',
'.phoenix-skybar.no-phoenix-skybar { padding-top: 0px !important; }',
'div#following + hr { display: none; }',
'#tweeting_button { white-space: nowrap !important; }',
// 「新しいツイートが…」を押した時の目印のやつ
'.last-on-refresh-after, .last-on-refresh-after:hover { background: #eee; padding: 0 !important; height: 10px; }',
'#label-scroll-to-tweet { cursor: pointer; position: absolute; right: 15px; margin-top: 0.4em; }',
'#label-scroll-to-tweet input { vertical-align: middle; margin-right: 2px; }',
'#favorites #label-scroll-to-tweet,',
'#inbox #label-scroll-to-tweet,',
'#sent #label-scroll-to-tweet,',
'#retweets_by_others #label-scroll-to-tweet,',
'#retweets #label-scroll-to-tweet,',
'#retweeted_of_mine #label-scroll-to-tweet { display: none; }',
'#search #label-scroll-to-tweet { margin-top: -2em; }',
// top-navigation
'.top-navigation > li { position: relative; }',
// lists
'.top-navigation .lists > div { display: none; position: absolute; margin-top: 10px; left: -5px; z-index: 1000; background: #fff; border: 4px solid #ddd; -moz-border-radius: 5px; -webkit-border-radius: 5px; -moz-box-shadow: 0 1px 0 #aaa; -webkit-box-shadow: 0 1px 0 #aaa; }',
'.top-navigation .lists li { display: list-item; text-align: left; padding: 1px; }',
'.top-navigation .lists:hover > div { display: block; }',
'.top-navigation .lists.loading > div { display: none !important; }',
'.top-navigation ul a { display: block; padding: 0.5em 1em; min-width: 100px; text-decoration: none !important; outline: 0; }',
'.top-navigation ul a:hover { background: #eee; }',
'.top-navigation ul .lock-icon { margin-left: 0.5em; }',
'.top-navigation .stat_count { display: none; padding: 0; }',
'.top-navigation .stat_count.unread { display: inline; font-weight: bold; }',
'.top-navigation .stat_count.unread:before { content: "("; }',
'.top-navigation .stat_count.unread:after { content: ")"; }',
// status.mentions
// '.status.unread .status-content { font-weight: bold; }',
'.mentions { background-color: ' + color['replyToMe'] + '; }',
'.status.in_reply_to.mentions { background-color: inherit !important; }',
'#replies .status.mentions { background-color: inherit !important; }',
'#replies .status.in_reply_to.mentions .status-body { background-color: ' + color['inReplyTo'] + ' !important; }',
// status.mine
'.mine { background-color: ' + color['mine'] + '; }',
'.status.in_reply_to.mine { background-color: inherit !important; }',
'#replies .status.mine { background-color: inherit !important; }',
'#profile .status.mine { background-color: inherit !important; }',
'#show .status.mine { background-color: inherit !important; }',
// status.in_reply_to
'.status.in_reply_to:hover { background: inherit !important; }',
'.has-reply { margin-bottom: 6px !important; }',
'.in_reply_to .actions-hover { bottom: 4px; }',
'.in_reply_to { position: relative; margin: 0 !important; margin-top: 3px !important; margin-bottom: 0 !important; padding: 0 0 4px 24px !important; border-bottom: 0 !important; opacity: 0; }',
'.in_reply_to.latest-reply { padding-top: 3px !important; display: none; }',
'.in_reply_to.slide-down { display: none; }',
'.in_reply_to .entry-content { font-size: 85% !important; }',
'.in_reply_to .status-body { position: relative; min-height: 48px !important; width: 405px !important; margin-left: 56px !important; padding: 6px 10px; border: 1px solid #ddd; -moz-border-radius: 5px; -moz-box-shadow: 1px 1px 0 #ccc; -webkit-border-radius: 5px; -webkit-box-shadow: 1px 1px 0 #ccc; border-radius: 5px; box-shadow: 1px 1px 0 #ccc; background: ' + color['inReplyTo'] + '; }',
'.in_reply_to.mine .status-body { background-color: ' + color['mine'] + ';}',
'.in_reply_to.mentions .status-body { background-color: ' + color['replyToMe'] + ';}',
'.in_reply_to .status-content { display: block; padding-right: 20px; overflow: visible !important; }',
// icon
'ol.statuses .in_reply_to .thumb { left: 24px; !important; }',
// thumbnail
'span.thumbnail { display: block; z-index: 50; }',
'.thumbnail:hover { z-index: 100; }',
'.thumbnail a { position: relative; display: inline-block; min-width: 14px; min-height: 14px; }',
'.thumbnail img { display: inline-block; max-width: 200px; max-height: 200px; border: 4px solid #eee; opacity: 0; }',
'.thumbnail a.loading { margin-right: 25px; margin-top: 10px; }',
'.thumbnail a.loading img { display: none; opacity: 0; -moz-transform: rotate(-25deg); -webkit-transform: rotate(-25deg); }',
'.thumbnail a:hover { z-index: 500; }',
'.status:hover .in_reply_to { background-color: inherit !important; }',
// more-replies
'.more-replies { display: block; min-height: 3px; font-size: 80%; margin: 0 !important; padding: 0 !important; text-align: center !important; }',
'.more-replies a { display: inline-block; height: 20px; cursor: pointer; }',
'.more-replies.loading { height: 20px; }',
'.more-replies.loading a { text-indent: -10000px; }',
'.more-replies.error { padding: 3px !important; color: #e55; }',
'.more-replies:hover { background: inherit !important; }',
// actions-hover
'.actions-hover .rt, .actions-hover .dm { display: block; float: left; line-height: 16px; }',
'.actions-hover .icon { cursor: pointer; }',
'.actions-hover .rt:hover .retweet-icon { background-position: -192px 0; }',
'.actions-hover .dm:hover .reply-icon { background-position: -16px 0; }',
'.actions-hover li:hover { background-color: transparent !important; }',
'.actions-hover li:hover a { text-decoration: underline; }',
// bitly
'a.bitly { position: relative; }',
'.bitly.long-url { position: absolute; z-index: 1000; padding: 0.7em 1em; display: none; background: #fff; border: 4px solid #ddd; -moz-border-radius: 5px; -webkit-border-radius: 5px; -moz-box-shadow: 0 1px 0 #aaa; -webkit-box-shadow: 0 1px 0 #aaa; }',
'.bitly.long-url .long-url { display: block; text-align: left; }',
'.bitly img { border: 1px solid #ddd; margin-top: 3px; max-height: 150px; max-width: 200px; }',
'.bitly .amazon { border: 0; }',
'.bitly .thumbshots { display: block; text-align: left; }',
'.bitly .divvy, .lists .divvy { display: block; position: absolute; width: 27px; height: 15px; top: -15px; left: 5px; background: url("data:image/gif;base64,R0lGODlhGwAPAJECAP///93d3f///wAAACH5BAEAAAIALAAAAAAbAA8AAAIwlI+pyxgNVZixzlvb3Vlu3hnfGI5mZgInlAIqybTu+znmNOf3juc6P/IJgRuh0VQAADs=") no-repeat center top; }',
// '.bitly.long-url.loading { display: block; }',
// '.bitly.long-url.visible { display: block; }',
'.bitly.long-url .icon { padding-left: 20px; display: none; }',
'.bitly.long-url.loading .icon { margin-top: 5px; padding-left: 20px; min-width: 20px; min-height: 12px; display: block !important; text-align: left;background-position: left center !important; }',
'.bitly.long-url.loading .thumbshots { display: none; }',
// favotter
'#favotter #new_results_notification,',
'#favotter #label-scroll-to-tweet,',
'#favotter #pagination { display: none; }',
'#favotter .status { padding: 8px 0 !important; }',
'#favotter .status p { margin: 0 0 0.5em 0 !important; }',
'#favotter .status p > a { font-weight: bold; }',
'#favotter .status .favotters { text-align: right; font-size: 85%;}',
'#favotter .status img { vertical-align: middle; }',
'#favotter .profile-user + .section { padding: 10px !important; }',
// favstar
'#favstar #new_results_notification,',
'#favstar #label-scroll-to-tweet,',
'#favstar #pagination { display: none; }',
'#favstar .profile-user + .section { padding: 10px !important; }',
'#favstar .mainAvatar { width: 48px; height: 48px; position: absolute; margin-left: -56px; }',
'#favstar .theTweet { margin-bottom: 0.5em; }',
'#favstar .clear, #favstar .tweetBy { display: none; }',
'#favstar .favouritesCount.fav,',
'#favstar .favouritesCount.favs,',
'#favstar .favouritesCount.retweet,',
'#favstar .favouritesCount.retweets { display: inline-block; visibility: visible; width: auto; height: auto; background-image: none; vertical-align: top; margin-left: 2px; font-size: 90%; }',
'#favstar .favouritesCount.retweet:after { content: " RT"; }',
'#favstar .favouritesCount.retweets:after { content: " RTs"; }',
'#favstar .favouritesCount.fav:after { content: " Fav"; }',
'#favstar .favouritesCount.favs:after { content: " Favs"; }',
'#favstar .avatarList { text-align: right; margin-bottom: 1px; }',
'#favstar .avatarList img { width: 24px; height: 24px; margin-left: 1px; }',
// loading gif
'.status .loading, .more-replies.loading, .top-navigation li.loading, .bitly.long-url.loading .icon { background: url("data:image/gif;base64,R0lGODlhDgAOAKIAAP///+/v797e3r29vTHOAP///wAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQFBwAFACwAAAAADgAOAAADK1i6APuwwRWCkmWMV63UG2ct2mQ+QipM4KCuUHvOJRnaWwk+dX/Hv1PtkQAAIfkEBQcABQAsAAAAAAwACAAAAxpYuhH7zEEhVFMAKFpv1lu1ZFCpDOhgFqlqJgAh+QQFBwAFACwCAAAADAAIAAADGlhaIvurwTKGkiUEVa3UG2ct2mSeCqAC5wokACH5BAUHAAUALAYAAAAIAAwAAAMaOFPc2m4VIdijFVLIe/gBBIYdBADdiXInkwAAIfkEBQcABQAsBgACAAgADAAAAxtYWjOrzb32qhVYvKztC8ECAAUYFqMCKmnVPgkAIfkEBQcABQAsAgAGAAwACAAAAxtYujo+jD0YawGACbFwDkGxcVdWgMpWoZbCMgkAIfkEBQcABQAsAAAGAAwACAAAAxoICtXei71JawhvDHexEIW2FVfzNRp1ViboJAAh+QQJBwAFACwAAAAADgAOAAADH1i63P4KAMjkpPHiHXrA3reNiyBs5jkMkKmsI7zJTAIAOw==") no-repeat center top; }',
'.top-navigation li.loading { color: inherit; margin-right: 5px; padding-right: 15px; background-position: right center !important; }',
'#oflotter-new-version { display: none; position: fixed; left: 1.5em; top: -1px; z-index: 10000; background: #f6f6f6; background: -moz-linear-gradient(top, #f6f6f6, #ccc); background: -webkit-gradient(linear, left top, left bottom, from(#f6f6f6), to(#ccc)); color: #333; padding: 4px 10px; -moz-box-shadow: 0 1px 1px rgba(0,0,0,0.7); -webkit-box-shadow: 0 1px 1px rgba(0,0,0,0.7); vertical-align: middle; font-weight: bold; -moz-border-radius: 0 0 5px 5px; -webkit-border-radius: 0 0 5px 5px; border-radius: 0 0 5px 5px;}'
];
function init() {
$( 'head' ).append( '<style type="text/css">' + styles.join( '' ) + '</style>' );
Nav.init();
InReplyTo.init();
TweetUrl.init();
Trends.init();
Refresh.init();
// ごにょごにょ
$( '#side_ad_base' ).remove();
if ( $( '#side > p.promotion:first' )[0] ) {
$( '#home_tab' ).css( { marginTop: '5px' } );
$( '#side > p.promotion' ).remove();
}
}
var Refresh = function() { return {
delay: 15000,
timer: null,
recent: '',
init: function() {
if ( !/^(?:home|replies|profile)/.test( document.body.id ) ) return;
setTimeout( function() {
$( '#new_results_notification' ).remove();
}, 3000 );
Refresh.timer = setInterval( function() {
// ホームとリプライ・プロフィールだけ
if ( !/^(?:home|replies|profile)/.test( document.body.id ) ) return;
Refresh.getJSON();
}, Refresh.delay );
},
getJSON: function() {
if ( !Refresh.recent ) {
Refresh.recent = $( '#timeline' ).find( 'li.status:first' ).attr( 'id' ).replace( 'status_', '' );
}
$.ajax( {
type: 'GET',
url: '/' + ( document.body.id == 'profile' ? pageScreenName : document.body.id ) + '?since_id=' + Refresh.recent + '&refresh=true&oflotter=true',
dataType: 'json',
success: function( json ) {
Refresh.addStatus( json );
},
error: function( xmlhttp ) {
// console.debug( xmlhttp.status + ': ' + xmlhttp.statusText );
}
} );
},
addStatus: function( json ) {
// とりあえず適当な場所に要素追加しとく
if ( !document.getElementById( 'oflotter-buffered' ) ) {
$( document.body ).append( '<div id="oflotter-buffered"></div>' );
}
var buffered = $( '#oflotter-buffered' );
buffered.append( json['#timeline'] );
// 最期に付け足したやつ
var statuses = buffered.children( 'ol:last' );
// since_idのアレ
var id = statuses.children( 'li.status:first' ).attr( 'id' );
if ( id ) {
Refresh.recent = id.replace( 'status_', '' );
}
var tl = $( '#timeline' );
// 逆順でTLに追加していく
$( statuses.children( 'li.status' ).get().reverse() ).each( function() {
if ( !tl.children( '#' + this.id )[0] ) {
var status = $( this ).hide().addClass( 'in-queue' );
InReplyTo.queue( function() {
tl.find( 'li:first' ).before( status );
status.animate( { height: 'show', opacity: 1 }, 'normal', 'swing' ).removeClass( 'in-queue' );
} );
}
} );
InReplyTo.dequeue();
// クリア
// 更新ないときは残るけど次で消えるしいいか…
buffered.remove();
// 一番上の文字サイズ
if ( document.body.id == 'profile' ) {
$( '.latest-status' ).removeClass( 'latest-status' );
tl.children( '.status:first' ).addClass( 'latest-status' );
}
}
} }();
var InReplyTo = function() { return {
inProgress: false,
data: [],
delay: 100, // キューのアレ
timer: null,
dequeue: function() {
if ( InReplyTo.data.length == 0 ) return;
if ( $( '#timeline' ).children( 'li' ).is( ':animated' ) ) {
// 処理中っぽかったら待つ
InReplyTo.timer = setTimeout( function() { InReplyTo.dequeue(); }, InReplyTo.delay );
return;
}
// 追加された一番最初のから
( InReplyTo.data.shift() )();
},
queue: function( callback ) {
// 順番に実行したいやつ
if ( typeof callback == 'function' ) {
InReplyTo.data.push( callback );
}
},
init: function() {
if ( document.body.id == 'show' ) {
return;
}
$( '#content li.hentry' ).each( function() {
InReplyTo.getStatusId( $( this ) );
} );
var content = document.getElementById( 'content' );
if ( !content ) return;
content.addEventListener( 'DOMNodeInserted', function( e ) {
if ( e.target.nodeType != 1 ) return;
// elementノードだけ見る in_reply_to~は無視
var className = e.target.className;
if ( className.indexOf( 'hentry' ) != -1 && className.indexOf( 'in_reply_to' ) == -1 ) {
InReplyTo.getStatusId( $( e.target ) );
} else if ( e.target.className == 'statuses' ) {
$( e.target ).find( 'li.hentry' ).each( function() {
InReplyTo.getStatusId( $( this ) );
} );
}
}, false );
},
getStatusId: function( status ) {
if ( !status.hasClass( 'in_reply_to' ) ) {
status.find( '.entry-content a.username' ).each( function() {
// 自分宛クラス
if ( $( this ).text() == sessionScreenName ) {
status.addClass( 'mentions' );
// bufferedの時だけリプライ数見る
if ( status.hasClass( 'buffered' ) ) {
Nav.setReplyCount( status, 1 );
status.addClass( 'unread' );
status.click( Nav.readStatus )
}
return false;
}
} );
TweetUrl.bind( status );
// ログインしてたらボタン付け足したり
if ( sessionLoggedIn == 'y' ) {
var statusId = status.attr( 'id' ).replace( /^status_/, '' ),
screenName = status.attr( 'class' ).replace( /^.+\su-([a-zA-Z0-9_]+)\s.+$/, '$1' );
InReplyTo.addRT( status, screenName, statusId );
InReplyTo.addDM( status, screenName );
}
}
status.find( '.entry-meta > a:gt(0)' ).each( function() {
if ( /status\/[0-9]+/.test( this.href ) ) {
InReplyTo.getStatusJSON( status, this.href );
return false;
}
} );
},
getStatusJSON: function( status, href, more ) {
$.ajax( {
type: 'GET',
url: href,
dataType: 'json',
success: function( json ) {
InReplyTo.addStatus( status, json, more );
},
error: function( xmlhttp ) {
if ( more ) {
InReplyTo.addError( xmlhttp, more );
}
}
} );
},
addError: function( xmlhttp, more ) {
// エラーだったら loadingアイコン消してメッセージ表示してみる
more.removeClass( 'loading' ).addClass( 'error' );
more.children().remove();
more.append( 'in_reply_to_status : ' + xmlhttp.status + ' ' + xmlhttp.statusText );
},
addStatus: function( status, json, more ) {
var in_reply_to = '',
in_reply_url = '',
time = '',
screenName = json['user']['screen_name'],
statusId = json['id_str'];
if ( json['in_reply_to_status_id_str'] ) {
var in_reply_url = protocol + '//twitter.com/' + json['in_reply_to_screen_name'] + '/status/' + json['in_reply_to_status_id_str'];
in_reply_to = ' <a href="' + in_reply_url + '">' + locale[lang]['in_reply_to'].replace( '{screenName}', json['in_reply_to_screen_name'] ) + '</a>';
}
time = InReplyTo.getTime( json['create_at'] );
// テンプレート置換用
var patterns = {
'{screen_name}' : screenName,
'{profile_image_url}' : json['user']['profile_image_url'],
'{text}' : InReplyTo.addAnchor( json['text'] ),
'{id}' : statusId,
'{time}' : time,
'{created_at}' : json['created_at'],
'{source}' : json['source'],
'{in_reply_to}' : in_reply_to
};
// .status-body
var statusBody = template.join('');
for ( var name in patterns ) {
statusBody = statusBody.replace( new RegExp( name, 'g' ), patterns[ name ] );
}
// クラスどうにかする
var className = [
'hentry',
'u-' + json['user']['screen_name'],
( sessionScreenName == json['user']['screen_name'] ? 'mine' : '' ),
'status in_reply_to',
( status.hasClass( 'buffered' ) ? 'buffered' : '' ),
( !status.hasClass( 'in_reply_to' ) ? 'latest-reply' : 'slide-down' )
].join( ' ' );
// li#status_id
var li = document.createElement( 'li' );
li.id = 'status_' + json['id'];
li.className = className;
li.innerHTML = statusBody;
InReplyTo.queue( function() {
// ここからは順番に処理する
status.after( li );
li = $( li );
li.animate( { height: 'show', opacity: 1 }, 'normal', 'swing' );
TweetUrl.bind( li );
if ( sessionLoggedIn == 'y' ) {
// ログインしてたらRT, DM, Fav付ける
InReplyTo.addReply( li, screenName, statusId );
InReplyTo.addRT( li, screenName, statusId );
InReplyTo.addDM( li, screenName );
InReplyTo.addFav( li, statusId, json['favorited'] );
}
// mentions色分け
li.find( '.entry-content a.username' ).each( function() {
if ( this.textContent == sessionScreenName ) {
li.addClass( 'mentions' );
return false;
}
} );
var nextSibling = li.next( 'li:first' );
if ( !nextSibling.hasClass( 'more-replies' ) ) {
// もっと読むかもしれんけどただの線になるかもしれんやつ
var more = document.createElement( 'li' );
more.className = 'status more-replies' + ( status.hasClass( 'buffered' ) ? ' buffered' : '' );
li.after( more );
more = $( more );
}
if ( !status.hasClass( 'in_reply_to' ) ) {
// statusが親ステータスの時は一旦ここで止める
if ( !json['in_reply_to_status_id_str'] ) {
return;
}
// もっと読むリンク付ける
var a = document.createElement( 'a' );
a.innerHTML = locale[lang]['more'];
$( a ).click( function() {
more.addClass( 'loading' );
li.addClass( 'more-loaded' );
InReplyTo.getStatusJSON( li, in_reply_url, more );
$( this ).unbind( 'click' );
} );
more.append( a );
// statusが親ステータスの時は一旦ここで止める
return;
}
// もっと読むクリックしてから次のリプライない場合は横線にする
if ( !json['in_reply_to_status_id_str'] && nextSibling.hasClass( 'loading' ) ) {
nextSibling.removeClass( 'loading' );
nextSibling.children().remove();
}
if ( json['in_reply_to_status_id_str'] ) {
// 次のリプライ取得する
InReplyTo.getStatusJSON( li, in_reply_url );
}
} );
InReplyTo.dequeue();
},
addAnchor: function( text ) {
// <a>のやつ URL, Reply, Hashtagでアレ
var m = text.match( /http(?:s|)\:\/\/[\w\.\-\?\@\~\^\&\=\%\/\#\;!]+|(?:^|\s)\@[\w]+|(?:^|\s)\#[\w]+/g );
if ( m ) {
for ( var i = 0; i < m.length; i++ ) {
var str = m[i].replace( /(?:^\s|\s$)/g, '' );
if ( str[0] == 'h' ) {
var url = str.replace( /&(?!amp;)/g, '&' ).replace( /"/g, '"' );
text = text.replace( str, '<a class="tweet-url web" rel="nofollow" href="' + url + '">' + url + '</a>' );
} else if ( str[0] == '@' ) {
text = text.replace( str, '@<a class="tweet-url username" rel="nofollow" href="http://twitter.com/' + str.substring(1) + '">' + str.substring(1) + '</a>' );
} else if ( str[0] == '#' ) {
text = text.replace( str, '<a class="tweet-url hashtag" rel="nofollow" href="http://twitter.com/search?q=%23' + str.substring(1) + '">' + str + '</a>' );
}
}
}
return text;
},
addReply: function( status, screenName, statusId ) {
// 自分にReplyさみしす(´・ω・`)
if ( status.hasClass( 'mine' ) ) return;
var actions = status.find( '.actions-hover:first' );
if ( actions.hasClass( 'add-reply' ) ) return;
actions.addClass( 'add-reply' );
var content = '@' + screenName + ' ';
actions.append( [
'<li>',
'<span class="reply">',
'<span class="reply-icon icon"></span>',
'<a title="' + locale[lang]['reply_title'].replace( '{screenName}', screenName ) + '" href="/?status=' + content + '&in_reply_to=' + screenName + '&in_reply_to_status_id=' + statusId + '">' + locale[lang]['reply'] + '</a>',
'</span>',
'</li>'
].join( '' ) );
actions.find( 'span.reply' ).children().click( function() {
return InReplyTo.setTweetStatus( content, screenName, statusId );
} );
},
addRT: function( status, screenName, statusId ) {
// 非常識RT
var actions = status.find( '.actions-hover:first' );
if ( actions.hasClass( 'add-rt' ) ) return;
actions.addClass( 'add-rt' );
var content = ' RT @' + screenName + ': ' + status.find( '.entry-content:first' ).text();
// フッター付けるアレ使ってたら消しとく
var footer = status.find( '.entry-footer' )[0];
if ( footer ) content = content.replace( footer.textContent, '' );
var href = [
'/?status=' + content.replace( /&(?!(?:amp|gt|lt|quot|#x[0-9a-fA-F]+|#[0-9]+);)/g, '&' ).replace( /</g, '<' ).replace( />/g, '>' ),
'&in_reply_to=' + screenName,
'&in_reply_to_status_id=' + statusId,
].join( '' );
actions.append( '<li><span class="rt"><span class="retweet-icon icon"></span><a href="' + href + '" title="' + locale[lang]['rt_title'].replace( '{screenName}', screenName ) + '">' + locale[lang]['rt'] + '</a></span>' );
actions.find( 'span.rt' ).children().click( function() {
return InReplyTo.setTweetStatus( content, screenName, statusId, true );
} );
},
addDM: function( status, screenName ) {
// 自分にDMさみしす(´・ω・`)
if ( status.hasClass( 'mine' ) ) return;
var actions = status.find( '.actions-hover:first' );
if ( actions.hasClass( 'add-dm' ) ) return;
actions.addClass( 'add-dm' );
var content = 'D ' + screenName + ' ';
actions.append( '<li><span class="dm"><span class="reply-icon icon"></span><a href="/?status=' + content + '" title="' + locale[lang]['dm'].replace( '{screenName}', screenName ) + '">DM</a></span></li>' );
actions.find( 'span.dm' ).children().click( function() {
return InReplyTo.setTweetStatus( content );
} );
},
addFav: function( status, statusId, isFav ) {
var star = [ '<span class="actions">',
'<div>',
'<a id="status_star_' + statusId + '" class="fav-action ' + ( isFav ? 'fav' : 'non-fav' ) + '" title="' + locale[lang]['fav'] + '">  </a>',
'</div>',
'</span>'
].join( '' );
status.find( '.status-content > strong:first' ).after( star );
},
getTime: function( createAt ) {
// 時間取得
var timestamp = Math.floor( ( (new Date()).getTime() - ( new Date( createAt ) ).getTime() ) / 1000 );
if ( timestamp < 60 ) {
time = locale[lang]['time'][0].replace( '{time}', timestamp );
} else if ( timestamp < 3600 ) {
time = locale[lang]['time'][1].replace( '{time}', Math.floor( timestamp / 60 ) );
} else if ( timestamp < 86400 ) {
time = locale[lang]['time'][2].replace( '{time}', Math.floor( timestamp / 3600 ) );
} else if ( timestamp < 2592000 ) {
time = locale[lang]['time'][3].replace( '{time}', Math.floor( timestamp / 86400 ) );
} else {
time = locale[lang]['time'][4].replace( '{time}', timestamp );
}
return time;
},
setTweetStatus: function( content, in_reply_to, in_reply_to_status_id, isRT ) {
var status = $( '#status' )[0];
if ( !status ) return;
if ( content.indexOf( '@' ) === 0 ) {
// むーん…
content = content + status.value.replace( content.replace( /\s+$/, '' ), '' );
content = content.replace( /\s+$/, '' ) + ' ';
}
status.value = content;
// キャレット位置を RT: 先頭、Reply, DM: 末尾にする
if ( isRT ) {
status.selectionStart = 0;
status.selectionEnd = 0;
} else {
status.selectionStart = content.length;
status.selectionEnd = content.length;
}
smoothScroll( null, function() {
status.focus()
} );
if ( in_reply_to ) {
$( '#in_reply_to' ).val( in_reply_to );
}
if ( in_reply_to_status_id ) {
$( '#in_reply_to_status_id' ).val( in_reply_to_status_id );
}
return false;
}
} }();
var TweetUrl = function() { return {
imageHosts: [
'movapic.com',
'twitpic.com',
'yfrog.',
'nicovideo',
'nico.ms',
'youtube.com',
'instagr.am',
'flic.kr'
],
imageUrls: {
'movapic' : /http\:\/\/movapic\.com\/pic\/([a-zA-Z0-9]+)/,
'twitpic' : /http\:\/\/twitpic\.com\/([a-zA-Z0-9]+)/,
'yfrog' : /http\:\/\/yfrog\.[a-z]+\/([a-zA-Z0-9]+)/,
'nicoms' : /http\:\/\/(?:www\.nicovideo\.jp\/watch|nico\.ms)\/sm([0-9]+)/,
'youtube' : /http\:\/\/www\.youtube\.com\/watch\?.*v=([a-zA-Z0-9\-]+)/,
'imgur' : /http\:\/\/imgur\.com\/([a-zA-Z0-9]+)/,
'mobypic' : /http\:\/\/moby\.to\/([a-zA-Z0-9]+)/,
'imgly' : /http\:\/\/img\.ly\/([a-zA-Z0-9]+)/,
'twitgoo' : /http\:\/\/twitgoo\.com\/([a-zA-Z0-9]+)/,
'picim' : /http\:\/\/pic\.im\/([a-zA-Z0-9]+)/,
'owly' : /http\:\/\/ow\.ly\/i\/([a-zA-Z0-9]+)/,
'image' : /http\:\/\/([a-zA-Z0-9_%\-\+\/\?=#&,.]+\.(?:jpg|jpeg|png|gif))/
},
thumbUrls: {
'movapic' : 'http://image.movapic.com/pic/s_{id}.jpeg',
'twitpic' : 'http://twitpic.com/show/thumb/{id}',
'yfrog' : 'http://yfrog.com/{id}.th.jpg',
'nicoms' : 'http://tn-skr{number}.smilevideo.jp/smile?i={id}',
'youtube' : 'http://i.ytimg.com/vi/{id}/default.jpg',
'imgur' : 'http://i.imgur.com/{id}m.jpg',
'mobypic' : 'http://moby.to/{id}:medium',
'imgly' : 'http://img.ly/show/thumb/{id}',
'twitgoo' : 'http://twitgoo.com/thumb/{id}',
'picim' : 'http://pic.im/website/thumbnail/{id}',
'owly' : 'http://static.ow.ly/photos/thumb/{id}.jpg',
'image' : 'http://{id}'
},
init: function() {
$( document.body ).append( '<div class="bitly long-url"><span class="divvy"></span><a class="long-url"></a><span class="thumbshots"></span><span class="icon">loading...</span></div>' );
$( '.bitly.long-url' ).mouseout( function() {
$( this ).removeClass( 'visible' );
} );
},
isImage: function( url ) {
var returnValue = false;
for ( var i = 0; i < TweetUrl.imageHosts.length; i++ ) {
if ( url.indexOf( TweetUrl.imageHosts[i] ) != -1 ) {
returnValue = true;
break;
}
}
return returnValue;
},
bind: function( status ) {
var ignoreUrls = [ 'ustre.am', 'moi.st' ];
var oflowme = [ 't.co', 'htn.to', 'ow.ly', 'is.gd', '2br.in', 'amba.to', 'fc2.in' ];
var span = document.createElement( 'span' );
span.className = 'thumbnail';
status.find( '.entry-meta' ).before( span );
$( '#' + status[0].id + ' .tweet-url.web' ).each( function() {
var a = this, href = this.href;
if ( $( a ).hasClass( 'thumbnail' ) ) return true;
var host = href.replace( /^http:\/\//, '' ).replace( /^([^\/]+).+$/, '$1' );
// 画像サムネすぐ取得できるやつ
for ( var name in TweetUrl.imageUrls ) {
if ( TweetUrl.imageUrls[ name ].test( href ) ) {
var id = RegExp.$1;
var src = TweetUrl.thumbUrls[ name ].replace( '{id}', id );
if ( name == 'nicoms' ) {
src = src.replace( '{number}', ( parseInt( id ) % 4 + 1 ) );
}
TweetUrl.addThumbnail( status, a, src );
}
}
if ( host == 'instagr.am' ) {
// Instagram
TweetUrl.instagram( status, a );
} else if ( /plixi|tweetphoto/.test( host ) ) {
// plixi
var src = 'http://api.plixi.com/api/TPAPI.svc/imagefromurl?size=thumbnail&url=' + encodeURIComponent( href );
TweetUrl.addThumbnail( status, a, src );
} else if ( host == 'flic.kr' ) {
// Flickr
TweetUrl.flickr( status, a );
} else if ( host.indexOf( 'www.amazon' ) === 0 ) {
// Amazon
TweetUrl.amazon( status, a );
} else if ( host == 'tinyurl.com' ) {
// Tinyurl
TweetUrl.tinyurl( a );
} else if ( oflowme.indexOf( host ) != -1 ) {
// t.co ( oflow.me )
TweetUrl.tco( a );
} else if ( host.length > 8 || ignoreUrls.indexOf( host ) != -1 ) {
return;
} else {
// Bit.ly
TweetUrl.bitly( a );
}
} );
},
addThumbnail: function( status, elm, src ) {
$( elm ).addClass( 'thumbnail' );
var a = $( document.createElement( 'a' ) );
status.find( 'span.thumbnail' ).append( a );
a.addClass( 'loading' ).attr( 'href', elm.href ).append( '<img src="' + src + '" />' );
a.find( 'img' )
.error( function() { $( this.parentNode ).remove(); } )
.load( function() {
$( this ).animate( { opacity: 1 } ).parent( 'a' ).removeClass( 'loading' );
} );
},
amazon: function( status, elm ) {
if ( /\/(?:dp|ASIN|gp\/product)\/([0-9A-Z]{10})/.test( elm.href ) ) {
var src = 'http://images-jp.amazon.com/images/P/' + RegExp.$1 + '.09._AA90_.jpg';
TweetUrl.addThumbnail( status, elm, src );
}
},
tco: function( elm ) {
$.getJSON(
'http://oflow.info/php/tco/?callback=?',
{
url: elm.href
},
function( json ) {
if ( !json['long_url'] ) return;
TweetUrl.setLongUrl( elm.href, json['long_url'] );
}
);
$( elm ).mouseover( function() {
$( this ).addClass( 'current' );
} );
},
bitly: function( elm ) {
$.getJSON(
'http://api.bit.ly/v3/expand?callback=?',
{
format: 'json',
login: 'oflow',
apiKey: 'R_8230b090c9fabb7a4eeef888ae06fe9d',
shortUrl: elm.href
},
function( json ) {
if ( json['status_code'] != 200 ) return;
var expand = json['data']['expand'];
if ( !expand || expand[0]['error'] ) {
return;
}
TweetUrl.setLongUrl( elm.href, expand[0]['long_url'] );
}
);
$( elm ).mouseover( function() {
$( this ).addClass( 'current' );
} );
},
tinyurl: function( elm ) {
$( elm ).mouseover( function() {
var a = this;
$( a ).addClass( 'current' );
$.getJSON(
'http://ss-o.net/api/reurl.json?callback=?',
{
url: a.href
},
function( json ) {
if ( !json['url'] ) return;
TweetUrl.setLongUrl( a.href, json['url'] );
}
);
} );
},
flickr: function( status, elm ) {
if ( !/^http:\/\/flic\.kr\/p\/([^\/]+)/.test( elm.href ) ) {
return;
}
$.getJSON(
'http://oflow.info/php/flickr/?callback=?',
{
p: RegExp.$1
},
function ( json ) {
if ( !json['image'] ) {
return;
}
TweetUrl.addThumbnail( status, elm, json['image'] );
}
);
},
instagram: function( status, elm ) {
if ( !/^http:\/\/instagr\.am\/p\/([^\/]+)/.test( elm.href ) ) {
return;
}
$.getJSON(
'http://oflow.info/php/instagram/?callback=?',
{
p: RegExp.$1
},
function ( json ) {
if ( !json['image'] ) {
return;
}
TweetUrl.addThumbnail( status, elm, json['image'] );
}
);
},
setLongUrl: function( shortUrl, longUrl ) {
longUrl = longUrl.replace( /\/g, '' );
shortUrl = shortUrl.replace( /\/g, '' );
$( 'a[href="' + shortUrl + '"]' ).each( function() {
var a = $( this );
if ( a.hasClass( 'expanded' ) ) {
return;
}
// href属性変えとく
a.attr( { href: longUrl } ).addClass( 'expanded' ).unbind( 'mouseover' );
if ( a.hasClass( 'current' ) ) {
a.removeClass( 'current' );
TweetUrl.getThumbnailUrl( a );
}
a.hover( function() {
TweetUrl.getThumbnailUrl( this );
}, function() {
$( '.bitly.long-url' ).removeClass( 'loading' ).fadeOut( 'fast' );
} );
if ( TweetUrl.isImage( longUrl ) ) {
// 短縮URLが画像の時もあるかもれない
TweetUrl.bind( a.parents( 'li.status' ) );
}
} );
},
getThumbnailUrl: function( elm ) {
var longUrl = elm.href;
var url = null;
for ( var name in oflotter.TweetUrl.imageUrls ) {
if ( !oflotter.TweetUrl.imageUrls[ name ].test( longUrl ) ) {
continue;
}
var id = RegExp.$1;
url = oflotter.TweetUrl.thumbUrls[ name ].replace( '{id}', id );
if ( name == 'nicoms' ) {
url = url.replace( '{number}', ( parseInt( id ) % 4 + 1 ) );
}
break;
}
if ( url ) {
var img = '<img src="' + url + '" />';
} else if ( longUrl.indexOf( 'http://www.amazon.co' ) === 0 && /\/(?:dp|ASIN|gp\/product)\/([0-9A-Z]{10})/.test( longUrl ) ) {
longUrl = decodeURIComponent( longUrl );
var img = '<img src="http://images-jp.amazon.com/images/P/' + RegExp.$1 + '.09._AA90_.jpg" class="amazon" />';
} else if ( longUrl.indexOf( 'http://foursquare.com' ) === 0 && /\/venue\/([0-9]+)/.test( longUrl ) ) {
$.getJSON(
'http://pipes.yahoo.com/pipes/pipe.run?_callback=?',
{
_id: 'b5365dcc66fafe5065965955223bf9d9',
_render: 'json',
url: 'http://api.foursquare.com/v1/venue.xml?vid=' + RegExp.$1,
},
function( json ) {
if ( !json['count'] ) {
$( '.bitly.long-url' ).removeClass( 'loading' );
return;
}
var venue = json['value']['items'][0];
var latlong = venue['geolat'] + ',' + venue['geolong'];
var img = '<img src="http://maps.google.com/staticmap?center=' + latlong + '&markers=' + latlong + '&zoom=14&size=200x150&key=ABQIAAAAHdQyPd3hIpryOWXAVYgxbxSnhvsz13Tv4UkZBHR3eJwOymtuUxRFPv_xNTkVG_XSgQYsQyeUWLMLCQ" class="4sq" />';
TweetUrl.setThumbnail( elm, img );
}
);
TweetUrl.showUrl( elm, longUrl );
return;
} else {
var img = '<img src="http://open.thumbshots.org/image.pxf?url=' + longUrl + '" />';
}
if ( /=UTF-8/i.test( longUrl ) ) {
longUrl = decodeURIComponent( longUrl );
}
TweetUrl.showUrl( elm, longUrl );
TweetUrl.setThumbnail( elm, img );
},
showUrl: function( elm, longUrl ) {
$( '.bitly.long-url' ).addClass( 'loading' ).fadeIn( 'fast' ).show();
$( '.bitly.long-url > .long-url' ).html( ( longUrl ? longUrl : '' ) );
var pos = $( elm ).offset();
$( '.bitly.long-url' ).css( { left: pos.left + 'px', top: pos.top + $( elm ).height() + 10 + 'px' } );
},
setThumbnail: function( elm, img ) {
$( '.bitly.long-url > .thumbshots' ).html( img );
$( '.bitly.long-url > .thumbshots img' ).load( function() {
$( '.bitly.long-url' ).removeClass( 'loading' );
$( this ).unbind();
} ).error( function() {
$( '.bitly.long-url' ).removeClass( 'loading' );
$( this ).unbind();
} );
}
} }();
var Nav = function() { return {
init: function() {
if ( sessionLoggedIn == 'n' ) return;
this.overwriteHome();
this.bindKeydownSubmit();
this.addFav();
this.addLists();
this.addReply();
if ( location.href.indexOf( '#favotter' ) != -1 ) {
Favotter.getJSON();
}
},
bindKeydownSubmit: function() {
// 入力フォーム Ctrl + Enter で普通な送信
// Alt + Enter で強引な送信 (画面遷移するけどin_reply_to付きのRTとか出来ちゃうかも)
$( '#status' ).keydown( function( e ) {
if ( e.ctrlKey && e.keyCode == 13 ) {
$( '#tweeting_button' ).trigger( 'click' );
} else if ( e.altKey && e.keyCode == 13 ) {
document.getElementById( 'status_update_form' ).submit();
}
} );
},
overwriteHome: function() {
// 右上ナビゲーションのホームをクリックしたときの動作変更
var homeTab = $( '#home_tab' );
var navHome = $( '.top-navigation > li:first' );
navHome.addClass( 'home' );
if ( !homeTab[0] ) return;
$( '#home_link' ).click( function() {
$( '#home_tab > a:first' ).trigger( 'click' );
Nav.resetReplyCount();
navHome.addClass( 'loading' );
var timer = setInterval( function() {
if ( !$( '#home_tab' ).hasClass( 'loading' ) ) {
navHome.removeClass( 'loading' );
clearInterval( timer );
}
}, 400 );
return false;
} );
},
addReply: function() {
// 右上ナビゲーションにリプライを追加する
var home = $( '.top-navigation > .home' );
home.after( [
' <li class="replies">',
'<a id="replies_link" href="/replies">@' + sessionScreenName + '</a>',
'<a class="stat_count" title="' + locale[lang]['reset_count'] + '"></a>',
'</li>'
].join( '' ) );
if ( $( '#replies_tab' )[0] ) {
$( '#replies_tab > a:first' ).append( '<span class="stat_count">0</span>' );
$( '.top-navigation .stat_count' ).click( function() {
Nav.resetReplyCount();
} );
var navReply = $( '.top-navigation > .replies' );
$( '#replies_link' ).click( function() {
$( '#replies_tab > a:first' ).trigger( 'click' );
navReply.addClass( 'loading' );
Nav.resetReplyCount();
var timer = setInterval( function() {
if ( !$( '#replies_tab' ).hasClass( 'loading' ) ) {
navReply.removeClass( 'loading' );
clearInterval( timer );
}
}, 400 );
return false;
} );
}
},
setReplyCount: function( status, count ) {
if ( !$( '#replies_tab' )[0] ) return;
var navCount = $( '.top-navigation .stat_count' ),
currentCount = navCount.html();
if ( currentCount ) {
count += Number( currentCount.replace( /[^\d]/g, '' ) );
}
count = Math.max( 0, count );
if ( count ) {
navCount.addClass( 'unread' ).html( count );
$( '#replies_tab .stat_count' ).html( count );
} else {
Nav.resetReplyCount();
}
},
readStatus: function() {
Nav.setReplyCount( $( this ), -1 );
$( this ).removeClass( 'unread' );
$( this ).unbind( 'click', Nav.readStatus );
},
resetReplyCount: function() {
$( '.top-navigation .stat_count' ).removeClass( 'unread' ).html( '' );
$( '#replies_tab .stat_count' ).html( '0' );
},
addLists: function() {
// 右上ナビゲーションにリストを追加する
var home = $( '.top-navigation > .home' );
home.after( [
' <li class="lists">',
'<a id="lists_link" href="/' + sessionScreenName + '/lists">' + locale[lang]['lists'] + '</a>',
'</li>'
].join( '' ) );
if ( $( '#side_lists .lists-links a > span:first' )[0] ) {
$( '.top-navigation .lists' ).append( '<div><span class="divvy"></span><ul></ul></div>' );
if ( pageScreenName ) {
$( '.top-navigation .lists ul' ).append( '<li><a href="/' + pageScreenName + '/lists">' + locale[lang]['lists_title'].replace( '{screenName}', pageScreenName ) + '</a></li>' );
}
$( '#side_lists .lists-links a > span' ).each( function() {
var list = $( this.parentNode );
var li = document.createElement( 'li' );
var a = document.createElement( 'a' );
li.appendChild( a );
$( '.top-navigation .lists ul' ).append( li );
a = $( a ).attr( 'href', list.attr( 'href' ) ).html( list.html() );
var navLists = $( '#lists_link' ).parent( 'li' );
if ( pageScreenName ) {
return true;
}
a.click( function() {
Nav.resetReplyCount();
list.trigger( 'click' );
navLists.addClass( 'loading' );
listLi = $( list[0].parentNode );
var timer = setInterval( function() {
if ( !listLi.hasClass( 'loading' ) ) {
navLists.removeClass( 'loading' );
clearInterval( timer );
}
}, 400 );
return false;
} );
} );
}
},
addFav: function() {
var home = $( '.top-navigation > .home' );
// ふぁぼったー
/* home.after( [
' <li class="favotter">',
'<a id="favotter_link" href="#">' + ( pageScreenName ? pageScreenName + '\'s ' : '' ) + locale[lang]['favotter'] + '</a>',
'</li>'
].join( '' ) );
$( '#favotter_link' ).click( function() {
$( this ).parent( 'li' ).addClass( 'loading' );
Fav.getJSON( 'favotter' );
} );
*/
// favstar
home.after( [
' <li class="favstar">',
'<a id="favstar_link" href="#">' + ( pageScreenName ? pageScreenName + '\'s ' : '' ) + locale[lang]['favstar'] + '</a>',
'</li>'
].join( '' ) );
$( '#favstar_link' ).click( function() {
$( this ).parent( 'li' ).addClass( 'loading' );
Fav.getJSON( 'favstar' );
} );
}
} }();
var Trends = function() { return {
timer: null,
init: function() {
var trend = $( '#local_trend_locations' );
if ( !trend[0] ) return;
// $( '.buzztter.ja' ).click() だと思うように動作しないんで onclick 使ってる
trend.children( 'p:first' ).before(
[
'<label>Others</label>',
'<ul id="local_trend_others" class="clearfix">',
'<li><a class="buzztter ja" href="javascript:void(0);" onclick="oflotter.Trends.getJSON(\'buzztter\',\'ja\')">buzztter (ja)</a></li>',
'<li><a class="buzztter en" href="javascript:void(0);" onclick="oflotter.Trends.getJSON(\'buzztter\',\'en\')">buzztter (en)</a></li>',
'<li><a class="favotter best" href="javascript:void(0);" onclick="oflotter.Trends.getJSON(\'favotter\')">ふぁぼったー</a></li>',
'</ul>',
'<hr />'
].join( '' )
);
},
clear: function() {
clearInterval( oflotter.Trends.timer );
oflotter.Trends.timer = null;
},
getJSON: function( site, mode ) {
oflotter.Trends.clear();
if ( !site ) return;
// とりあえず buzztter と ふぁぼったーの人気
if ( site == 'buzztter' ) {
if ( [ 'ja', 'en' ].indexOf( mode ) == -1 ) {
mode = 'ja';
}
var text = 'buzztter' + ( mode != 'ja' ? ' (en)' : '' );
var url = 'http://buzztter.com/' + mode + '/rss';
// buzztterは30秒ごとにリロードしてるけどRSS取得は1分ごとに
oflotter.Trends.timer = setInterval( function() {
if ( $( '#location_menu' ).text().indexOf( 'buzztter' ) == -1 ) {
// buzztterじゃなければタイマー止める
oflotter.Trends.clear();
return;
}
oflotter.Trends.getJSON( site, mode );
}, 60000 );
} else {
site = 'favotter';
mode = 'best';
var text = oflotter.locale[ oflotter.lang ]['favotter'];
var url = 'http://favotter.net/home.php?env=rss&mode=best';
}
$( '#trends_loading').show();
// 若干タイミングずらす
setTimeout( function() {
$( '#local_trend_locations' ).find( 'li' ).removeClass( 'active' );
$( '#local_trend_others' ).find( '.' + site + '.' + mode ).parent( 'li' ).addClass( 'active' );
}, 300 );
$( '#location_menu' ).text( text );
$.getJSON(
'http://pipes.yahoo.com/pipes/pipe.run?_callback=?',
{
_id: 'eaf275b910e3d7421033ce6f3076cce0',
_render: 'json',
url: url
},
function( json ) {
if ( !json['count'] ) return;
oflotter.Trends.setTrends( site, json['value'] );
}
);
},
setTrends: function( site, value ) {
$( '#trends_loading').hide();
var items = value['items'];
var trendsLinks = $( '.sidebar-menu.trends-links' );
trendsLinks.empty();
trendsLinks.css( { opacity: 0 } );
for ( var i = 0; i < 15; i++ ) {
var item = items[i];
if ( !item ) break;
trendsLinks.append( '<li class="link-title ' + site + '"><a href="' + item[ 'link' ] + '">' + item[ 'title' ] + '</li>' );
}
trendsLinks.fadeTo( 'fast', 1 );
}
} }();
var Fav = function() { return {
// ふぁぼったー, favstarのやつまだ考え中
getJSON: function( site ) {
// プロフィールページ開いてたらそのユーザー、それ以外は自分のを取得する
var screenName = ( pageScreenName ? pageScreenName : sessionScreenName );
if ( !site ) site = 'favstar';
if ( site == 'favstar' ) {
$.getJSON(
'http://pipes.yahoo.com/pipes/pipe.run?_callback=?',
{
_id: '612ca02abf0884ef5640a376a87a9a7b',
_render: 'json',
user: screenName
},
function( json ) {
if ( !json['count'] ) {
$( '.top-navigation > li.favstar:first' ).removeClass( 'loading' );
return;
}
Fav.setFavstar( json['value']['items'] );
}
);
} else {
$.getJSON(
'http://pipes.yahoo.com/pipes/pipe.run?_callback=?',
{
_id: 'eaf275b910e3d7421033ce6f3076cce0',
_render: 'json',
url: 'http://favotter.net/user/' + screenName + '?env=rss&mode=new'
},
function( json ) {
if ( !json['count'] ) {
$( '.top-navigation > li.favotter:first' ).removeClass( 'loading' );
return;
}
Fav.setFavotter( json['value']['items'] );
}
);
}
},
setFavotter: function( items ) {
var tl = $( '#timeline' );
tl.empty();
document.body.id = 'favotter';
$( '#heading' ).html( locale[lang]['favotter'] );
for ( var i = 0, length = items.length; i < length; i++ ) {
var status = [
'<li class="hentry status favotter">',
'<span class="status-body">',
'<span class="status-content">',
items[i][ 'description' ],
'</span>',
'</span>',
'</li>'
].join( '' );
tl.append( status );
}
tl.append( '続きはWebで!' );
$( '.top-navigation > li.favotter:first' ).removeClass( 'loading' );
},
setFavstar: function( items ) {
var tl = $( '#timeline' );
tl.empty();
document.body.id = 'favstar';
var screenName = ( pageScreenName ? pageScreenName : sessionScreenName );
$( '#heading' ).html( screenName + '\'s recent favstar' );
for ( var i = 0, length = items.length; i < length; i++ ) {
var status = [
'<li class="hentry status favstar">',
'<span class="status-body">',
items[i][ 'content' ].replace( /favstar\.fm\/users/g, 'twitter.com' ).replace( / target="_blank"/g, '' ),
'</span>',
'</li>'
].join( '' );
tl.append( status );
}
$( '.top-navigation > li.favstar:first' ).removeClass( 'loading' );
}
} }();
// バージョンチェックする
var Version = function() { return {
current: version,
check: function( json ) {
if ( !json['version'] || oflotter.Version.current == json['version'] ) return;
$( document.body ).append( [
'<div id="oflotter-new-version">',
'<a href="http://oflow.info/files/oflotter.user.js">',
'oflotter ' + json['version'] + ' is released</a>',
'</div>'
].join( '' ) );
setTimeout( function() {
$( '#oflotter-new-version' ).animate( { height: 'show', opacity: 'show' }, 'fast' );
}, 3000 );
}
} }();
const protocol = window.location.protocol;
var locale = {
en: {
'home' : 'Home',
'reply' : 'Reply',
'reply_title' : 'reply to {screenName}',
'retweet' : 'Retweet',
'rt' : 'RT @:',
'rt_title' : 'RT @{screenName}:',
'lists' : 'Lists',
'lists_title' : '{screenName}\'s lists',
'dm' : 'Direct message {screenName}',
'fav' : 'favorite this tweet',
'source': 'via {source}',
'in_reply_to' : 'in reply to {screenName}',
'time' : [
'about {time} seconds ago',
'about {time} minutes ago',
'about {time} hours ago',
'about {time} days ago',
'meccha ago'
],
'more' : 'more',
'scroll' : 'scroll new tweet',
'reset_count': 'reset reply count',
'favotter' : 'Favotter',
'favstar' : 'favstar',
'shimeji': 'Shimeji'
},
ja: {
'home' : 'ホーム',
'reply' : '返信',
'reply_title' : '{screenName}に返事',
'retweet' : 'リツイート',
'rt' : '非公式RT',
'rt_title' : 'RT @{screenName}:',
'lists' : 'リスト',
'lists_title' : '{screenName}のリスト',
'dm' : '{screenName}にダイレクトメッセージ',
'fav' : 'ツイートをお気に入りに登録',
'source': '{source}から',
'in_reply_to' : '{screenName}宛',
'time' : [
'約{time}秒前',
'約{time}分前',
'約{time}時間前',
'約{time}日前',
'めっちゃ前'
],
'more' : 'もっと読む',
'scroll': '新しいツイートにスクロール',
'reset_count': 'リプライ数リセット',
'favotter' : 'ふぁぼったー',
'favstar' : 'favstar',
'shimeji': 'しめじ'
}
};
var lang = ( $( document.body ).hasClass( 'ja' ) ? 'ja' : 'en' );
var template = [
'<span class="thumb vcard author"><a class="tweet-url profile-pic url" href="' + protocol + '//twitter.com/{screen_name}"><img class="photo fn" width="48" height="48" src="{profile_image_url}" alt="{screen_name}" title="{screen_name}" /></a></span>',
'<span class="status-body">',
'<span class="status-content">',
'<strong><a class="tweet-url screen-name" href="' + protocol + '//twitter.com/{screen_name}">{screen_name}</a></strong> ',
'<span class="entry-content">{text}</span>',
'</span>',
'<span class="meta entry-meta"><a class="entry-date" href="' + protocol + '//twitter.com/{screen_name}/status/{id}" rel="bookmark"><span class="published timestamp" data="{time:\'{created_at}\'}">{time}</a> <span>' + locale[lang]['source'] + '</span>{in_reply_to}</span>',
'<ul class="actions-hover"></ul><ul class="meta-data clearfix"></ul>',
'</span>'
];
// セッション情報とか取得しとく
var sessionScreenName = $( 'meta[name="session-user-screen_name"]' ).attr( 'content' );
var sessionLoggedIn = $( 'meta[name="session-loggedin"]' ).attr( 'content' );
var pageScreenName = $( 'meta[name="page-user-screen_name"]' ).attr( 'content' );
init();
return {
Version: Version,
lang: lang,
locale: locale,
sessionLoggedIn: sessionLoggedIn,
sessionScreenName: sessionScreenName,
pageScreenName: pageScreenName,
TweetUrl: TweetUrl,
Fav: Fav,
Trends: Trends,
Refresh: Refresh,
Nav: Nav
}
};
// 旧UIならoflotter実行する
if ( document.body.className.indexOf( '-style-twttr' ) == -1 ) {
var script = document.createElement( 'script' );
script.id = 'oflotter';
script.type = 'text/javascript';
script.charset = 'UTF-8';
script.appendChild (
document.createTextNode( [
' $( ".fixed-banners" ).remove();',
'var oflotter = (' + oflotter_jquery.toString() + ')();',
'oflotter.Trends.getJSON( "buzztter", "ja" );',
'if ( $( document.body ).hasClass( "phoenix-skybar" ) ) {',
' $( document.body ).removeClass( "phoenix-skybar" );',
'}'
].join( '' ) )
);
document.body.appendChild( script );
var script = document.createElement( 'script' );
script.type = 'text/javascript';
script.charset = 'UTF-8';
script.src = 'http://oflow.info/files/oflotter.json?' + parseInt( (new Date ) / 1000 );
document.body.appendChild( script );
}