// ==UserScript== // @name ☆button // @description ☆ボタンのないはてなブログに☆ボタン出す // @include http://* // @require http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js // @require http://s.hatena.ne.jp/js/HatenaStar.js // ==/UserScript== /* * @title ☆button * @description ☆ボタンのないはてなブログに☆ボタン出す * @include http://* * @license MIT License * @require http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js * @require http://s.hatena.ne.jp/js/HatenaStar.js */ (function($) { var setupStar = function() { if (!Hatena.Star) return; var entryConfig = { entryNodes: { // PC, article 'article.entry': { uri: 'a.bookmark', title: 'h1.entry-title', container: 'div.hatena-star-container' }, // PC, archive 'section.archive-entry': { uri: 'a.entry-title-link', title: 'h1.entry-title', container: 'span.star-container' }, // touch, article 'li.entry-article': { uri: 'a.bookmark', title: '.entry-title', container: 'div.hatena-star-container' }, // touch, comment 'li.entry-comment': { uri: 'p.comment-metadata a.permalink', title: 'div.comment-content', container: '.comment-metadata' } } }; var commentConfig = { entryNodes: { 'li.entry-comment': { uri: 'p.comment-metadata a.permalink', title: 'div.comment-content::-ten-truncate', container: '.comment-metadata' } } }; Hatena.Star.SiteConfig = entryConfig; Hatena.Star.EntryLoader.intoCommentScope = function(func) { Hatena.Star.SiteConfig = commentConfig; func(); Hatena.Star.SiteConfig = entryConfig; }; Hatena.Star.User.prototype.userPage = function() { var uri = Hatena.Diary.data('admin-domain') + '/' + this.name + '/'; return uri; }; var img_src = Hatena.Diary.URLGenerator.static_url('/images/theme/star/hatena-star-add-button.png'); Hatena.Star.AddButton.ImgSrc = img_src; Hatena.Star.AddButton.SmartPhone.ImgSrc = img_src; }; setupStar(); Hatena.Diary.Star = { // スター大きくしてプロフィールアイコン重ねる initBigStar: function() { if (!Hatena.Star) return; // 体裁変更 Hatena.Star.Star.prototype.generateImg = function() { var img, star_img, user_img, span, user_icon; user_icon = function(name) { return "http://cdn1.www.st-hatena.com/users/" + (encodeURI(name.slice(0, 2))) + "/" + (encodeURI(name)) + "/profile.gif"; }; star_img = Hatena.Star.Star.getImage(this.container); star_img.alt = this.screen_name; star_img.title = ''; if (this.color && this.color != 'yellow' && this.color != 'temp') { star_img.alt = star_img.alt + ' (' + this.color + ')'; } this.img = star_img; if (this.screen_name) { if (!Hatena.Star.Star.gotImage[this.screen_name]) { Hatena.Star.Star.gotImage[this.screen_name] = {}; } if (!Hatena.Star.Star.gotImage[this.screen_name][this.color]) { span = document.createElement('span'); span.className = 'hatena-big-star-star-container'; user_img = document.createElement('img'); user_img.src = user_icon(this.screen_name); user_img.setAttribute('tabIndex', 0); user_img.className = 'hatena-star-user'; user_img.style.padding = '0'; user_img.style.border = 'none'; span.appendChild(user_img); span.appendChild(star_img); Hatena.Star.Star.gotImage[this.screen_name][this.color] = span; } this.img = Hatena.Star.Star.gotImage[this.screen_name][this.color].cloneNode(true); } }; // posの値の調整 Hatena.Star.AddButton.prototype.showColorPallet = function(e) { this.clearSelectedColorTimer(); if (!this.pallet) this.pallet = new Hatena.Star.Pallet(); var pos = Ten.Geometry.getElementPosition(this.img); pos.x += 2; pos.y += 22; this.pallet.showPallet(pos, this); }; Hatena.Star.AddButton.SmartPhone.prototype.showColorPallet = function(e) { this.clearSelectedColorTimer(); if (!this.pallet) { this.pallet = new Hatena.Star.Pallet.SmartPhone(); } var pos = Ten.Geometry.getElementPosition(this.img); pos.x = Ten.Browser.isDSi ? 5 : 15; pos.y += 30; this.pallet.showPallet(pos, this); this.pallet.show(Hatena.Star.UseAnimation ? {x: 0,y: 0} : pos); }; // スターボタン押されたら必要な情報をはてなブログのサーバーに送る Hatena.Star.AddButton.prototype.addStar = _.wrap(Hatena.Star.AddButton.prototype.addStar, function(fun, e) { fun.apply(this, [e]); Hatena.Diary.trackEvent('hatena-star-add'); }); Hatena.Star.AddButton.SmartPhone.prototype.addStar = _.wrap(Hatena.Star.AddButton.SmartPhone.prototype.addStar, function(fun, e) { fun.apply(this, [e]); Hatena.Diary.trackEvent('hatena-star-add-smartphone'); }); }, // スター付けたことない場合、ツールチップを表示する initStarNavigation: function() { if (!Hatena.Star) return; var TIPSY_ALLOW_OFFSET = 8; var $tipsy_arrow = $('
').text(Hatena.Locale.text('star-navigation-message'))) .prepend($tipsy_arrow); $.getJSON("https://www.hatena.ne.jp/notify/notices.count.json?services=1&callback=?", function(res) { // スター利用中ならなにもしない if (res.services.s) return; // はてなスター利用中ではないけどスターつけた Hatena.Star.AddButton.prototype.addStar = _.wrap(Hatena.Star.AddButton.prototype.addStar, function(fun, e) { fun.apply(this, [e]); Hatena.Diary.trackEvent('hatena-star-add-first'); }); // 既読かどうかを localStorage に保存する var localStorageKey = 'Hatena.Diary.Blogs.StarNavigation.Read'; var isRead = function() { try { return localStorage[localStorageKey] === 'already'; } catch (ignore) { } }; var setIsRead = function(isRead) { try { localStorage[localStorageKey] = isRead ? 'already' : 'not yet'; } catch (ignore) { } }; // 既読のとき tipsy 出す必要ない if (isRead()) return; var wait_for_star_load = function(func) { setTimeout(function() { var $star_button = $('.hatena-star-add-button'); if ($star_button.length !== 0) { func(); return; } wait_for_star_load(func); }, 200); }; wait_for_star_load(function() { $('.hatena-star-container').after($star_navigation_tooltip); $('.tipsy-arrow.blue').each(function() { $(this).css({left: $(this).parent().siblings().find('.hatena-star-add-button').offset().left - $star_navigation_tooltip.offset().left + TIPSY_ALLOW_OFFSET}); }); $('.star-navigation-tooltip').click(function() { setIsRead(true); $(this).fadeOut(1000); }); $('.hatena-star-add-button').click(function() { setIsRead(true); $('.star-navigation-tooltip').fadeOut(1000); }); }); }); }, initDeleteStar: function() { if (!Hatena.Star) return; Hatena.Star.Star.prototype.setImgObservers = function() { var self = this; new Ten.Observer(self.img, 'onmouseover', self, 'showName'); new Ten.Observer(self.img, 'onmouseout', self, 'hideName'); }; Hatena.Star.Star.prototype.asElement = _.wrap(Hatena.Star.Star.prototype.asElement, function(func) { var self = this; var element = func.call(self); if (!Hatena.Star.Config.isStarDeletable) return element; // 以下はスターがdocumentにappendされてから実行 // スター削除できるとき成功するDeferredを返す var checkCanDelete = _.once(function() { var dfd = $.Deferred(); var uri = Hatena.Star.BaseURL.replace(/^http:/, Hatena.Star.BaseURLProtocol) + 'star.deletable.json'; var data = { name: self.name, uri: self.entry.uri }; if (self.color) data.color = self.color; if (self.quote) data.quote = self.quote; $.ajax({ type: 'get', dataType: 'jsonp', url: uri, data: data }).done(function(res) { var can_delete = res.result && (res.message || res.confirm_html); if (can_delete) { dfd.resolve(res); } else { dfd.reject(res); } }); return dfd; }); var $star_container = $(self.anchor); $star_container.hover( function() { // 削除可能なとき,右上に削除ボタンを表示 checkCanDelete().done(function(deletion_info) { var $delete_button = $("").addClass('star-delete-button').attr( "src", Hatena.Diary.URLGenerator.static_url('/images/theme/star/star-delete.png') ); $star_container.append($delete_button); var button_position = $delete_button.position(); $delete_button.css({ left: button_position.left - 9, top: button_position.top }); //クリックされたら削除確認 $delete_button.click(function() { if (deletion_info.confirm_html) { var pos = Ten.Geometry.getElementPosition(self.anchor); var scr = new Hatena.Star.DeleteConfirmScreen(); scr.showConfirm(deletion_info.confirm_html, self, pos); } else if (confirm(deletion_info.message)) { self.deleteStar(); } return false; }); }); }, function() { $(this).find(".star-delete-button").remove(); }); return element; }); }, initSelectStar: function() { if (!Hatena.Star) return; var SELECT_STAR_OPACITY = 0.6, SELECT_STAR_OFFSET = 60, SELECT_STAR_SIZE = 32, GET_BIGGER_SIZE = 24.0; var $target_article; var $select_star = $('.select_star_button_container'); var $message_box = $('#select-star-message-box'); var using_select_star = false; var message_fadeout_timer; // スターボタンをクリックしたとき Hatena.Star.AddButton.prototype.addStar = _.wrap(Hatena.Star.AddButton.prototype.addStar, function(func, e) { this.copySelectedText(); func.call(this, e); if (this.selectedText !== '') { Hatena.Diary.trackEvent('hatena-star-add-quote'); } }); // スター投稿の結果が返ってきたとき Hatena.Star.AddButton.prototype.receiveResult = _.wrap(Hatena.Star.AddButton.prototype.receiveResult, function(func, args) { // メッセージを出す位置 var message_box_offset = { top: $select_star.offset().top, left: $select_star.offset().left + SELECT_STAR_SIZE }; if (using_select_star) { // ログインしてるときのみメッセージを出す if (args.is_guest) { var pos = {x: message_box_offset.left,y: message_box_offset.top}; // HatenaStar.js本体でウィンドウ出す位置を調整されるので, その分足し引きして調整を打ち消す pos.x += 10; pos.y -= 25; this.lastPosition = pos; } else { $message_box .stop() .css(_.extend(message_box_offset, {opacity: 1.0})) .show(); if (message_fadeout_timer) { clearTimeout(message_fadeout_timer); } message_fadeout_timer = setTimeout(function() { $message_box.fadeOut(1000); }, 1000); Hatena.Diary.trackEvent('hatena-star-add-select'); } } using_select_star = false; func.call(this, args); }); // テキストを選択したとき $(document).mouseup(function(e) { var $target = $(e.target); // deferしないとタイミング的にうまく選択領域を取得できない _.defer(function() { var selected_text = Ten.DOM.getSelectedText(); var selection_is_blank = (selected_text === ''); var is_article = $target.closest('article').length > 0; var is_comment = $target.closest('.comment').length > 0; var is_select_star = $target.closest('.select_star_button_container').length > 0; if (is_select_star) { return; } if (!is_article || is_comment || selection_is_blank) { $select_star.hide(); return; } $target_article = $target.closest('article'); $select_star.css({top: e.pageY,left: $target_article.offset().left - SELECT_STAR_OFFSET}); $select_star.show(); }); }); $select_star.on('mouseenter', function() { $(this).css({opacity: 1.0}); }); $select_star.on('mouseleave', function() { $(this).css({opacity: SELECT_STAR_OPACITY}); }); $('.select_star_button').on('click', function(e) { using_select_star = true; $(this) .stop() .animate({ width: SELECT_STAR_SIZE + GET_BIGGER_SIZE,height: SELECT_STAR_SIZE + GET_BIGGER_SIZE, top: -GET_BIGGER_SIZE / 2,left: -GET_BIGGER_SIZE / 2 }, 80) .animate({width: SELECT_STAR_SIZE,height: SELECT_STAR_SIZE,top: 0,left: 0}, 400); }); // HatenaStarから引用スターボタンにクリックハンドラをたてる Hatena.Star.AddButton.prototype.setupObservers = _.wrap(Hatena.Star.AddButton.prototype.setupObservers, function(func) { func.call(this); new Ten.Observer($select_star[0], 'onclick', this, 'beforeAddStar'); }); Hatena.Star.AddButton.prototype.beforeAddStar = function(e) { if ($target_article.attr('data-uuid') === $(this.entry.entryNode).attr('data-uuid')) { this.addStar(e); } }; }, // サードパーティクッキー送信されてないときmobile用のエントリページを新しいウィンドウで開く initStarForThirdPartyCookiesDisabled: function(info) { Hatena.Diary.Util.waitForResource(function() { return Hatena.Star; }, function() { _.extend(Hatena.Star.AddButton.prototype, { addStar: function() { var self = this; // uri: エントリのURI // location: スターつけたあとのリダイレクト先 var url = 'http://s.hatena.ne.jp/star.add?' + $.param({ uri: self.entry.uri, location: Hatena.Diary.data('admin-domain') + '/-/close' }); var position = { width: 500, height: 200 }; position.left = Math.floor((screen.width - position.width) / 2); position.top = Math.floor((screen.height - position.height) / 2); var position_string = Hatena.Diary.Util.positionToPositionString(position); var star_window = window.open(url, 'add_star', position_string); // window閉じられたら読み込みなおす Hatena.Diary.Util.waitForResource(function() { return star_window.closed; }, function() { Hatena.Diary.Util.updateDynamicPieces([self.entry.entryNode.parentNode]); }); }, // カラースターつけられないのでパレット開かなくする showColorPallet: function() { }, showColorPalletDelay: function() { } }); }); } }; })(jQuery); (function() { Hatena.Star.EntryLoader(); Hatena.Diary.Star.initBigStar(); Hatena.Diary.Star.initSelectStar(); Hatena.Diary.Star.initStarNavigation(); Hatena.Diary.Star.initDeleteStar(); }());