☆button

  • /*
     * @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 = $('<div>').addClass('tipsy-arrow blue'), 
          $star_navigation_tooltip = $('<div>').addClass('star-navigation-tooltip tipsy-n')
                .append($('<p>').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 = $("<img>").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();
    }());
    
  • Permalink
    このページへの個別リンクです。
    RAW
    書かれたコードへの直接のリンクです。
    Packed
    文字列が圧縮された書かれたコードへのリンクです。
    Userscript
    Greasemonkey 等で利用する場合の .user.js へのリンクです。
    Loader
    @require やソースコードが長い場合に多段ロードする Loader コミのコードへのリンクです。
    Metadata
    コード中にコメントで @xxx と書かれたメタデータの JSON です。

History

  1. 2014/05/22 15:29:02 - 2014-05-22