Google Bookmarks Nested Labels

Add the 'Nested Labels' feature to google bookmarks.

  1. // ==UserScript==
  2. // @name Google Bookmarks Nested Labels
  3. // @namespace https://github.com/Lorentz83
  4. // @description Add the 'Nested Labels' feature to google bookmarks.
  5. // @include http://www.google.com/bookmarks/*
  6. // @include https://www.google.com/bookmarks/*
  7. // @require http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.js
  8. // @version 1.3b
  9. // @grant none
  10. // @icon https://raw.githubusercontent.com/Lorentz83/userscripts/master/GoogleBookmarksNestedLabels/icon.png
  11. // @license GPLv2; http://www.gnu.org/licenses/
  12. // ==/UserScript==
  13.  
  14. /**
  15. * This program is free software: you can redistribute it and/or modify
  16. * it under the terms of the GNU General Public License as published by
  17. * the Free Software Foundation, either version 2 of the License, or
  18. * (at your option) any later version.
  19. *
  20. * This program is distributed in the hope that it will be useful,
  21. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  22. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  23. * GNU General Public License for more details.
  24. *
  25. * You should have received a copy of the GNU General Public License
  26. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  27. */
  28.  
  29. var containKeys = function(obj) {
  30. for (var key in obj) {
  31. return true;
  32. }
  33. return false;
  34. }
  35.  
  36. var Tag = function(label, fullLabel, href, initialOpen, bold) {
  37. this.label = label;
  38. this.fullLabel = fullLabel;
  39. this.href = href;
  40. this.subLabel = new Object();
  41. this.initialOpen = initialOpen;
  42. this.bold = bold;
  43. }
  44.  
  45. Tag.prototype.separator = '/';
  46.  
  47. Tag.prototype.addToTree = function(splittedLabel, fullLabel, href, initialOpen){
  48. if ( splittedLabel.length > 0 ) {
  49. if (this.subLabel[ splittedLabel[0] ] == undefined) {
  50. var bold = initialOpen && splittedLabel.length == 1; // is open and is the last
  51. //this must be fix if here there is a middle label
  52. this.subLabel[ splittedLabel[0] ] = new Tag(splittedLabel[0], fullLabel, href, initialOpen, bold);
  53. }
  54. this.initialOpen = this.initialOpen || initialOpen;
  55. this.subLabel[ splittedLabel[0] ].addToTree( splittedLabel.slice(1), fullLabel, href, initialOpen);
  56. }
  57. }
  58.  
  59. Tag.prototype.toLi = function (prefix) {
  60. if (prefix == undefined)
  61. prefix = '';
  62. var labelCssClass = 'item';
  63. if (this.bold) {
  64. labelCssClass = 'item selectedLabel';
  65. }
  66. var hasSubTree = containKeys(this.subLabel);
  67. var expandLinkText = '|';
  68. var olStyle = '';
  69. if (hasSubTree) {
  70. if (this.initialOpen){
  71. expandLinkText = '[-]';
  72. }
  73. else {
  74. expandLinkText = '[+]';
  75. olStyle = 'display:none;';
  76. }
  77. expandLinkText = '<span class="expandLink">' + expandLinkText + '</span>';
  78. }
  79. var toReturn = new Array();
  80. var i = 0;
  81. toReturn[i++] = prefix + '<li>';
  82. toReturn[i++] = prefix + ' <div class="expandLinkContainer">' + expandLinkText + '</div>';
  83. toReturn[i++] = prefix + ' <a href="' + this.href + '" title="' + this.fullLabel + '" class="' + labelCssClass + '">' + this.label + '</a>'
  84. if (hasSubTree) {
  85. toReturn[i++] = prefix + ' <ol class="subLabelsList" style="' + olStyle + '">';
  86. for (var label in this.subLabel) {
  87. toReturn[i++] = this.subLabel[label].toLi(prefix + ' ');
  88. }
  89. toReturn[i++] = prefix + ' </ol>';
  90. }
  91. toReturn[i++] = prefix + '</li>';
  92. return toReturn.join('\n');
  93. }
  94.  
  95. Tag.prototype.toString = function (prefix) {
  96. if (!prefix)
  97. prefix = '';
  98. var toReturn = prefix + this.label + '\n';
  99. for (var i in this.subLabel) {
  100. toReturn += this.subLabel[i].toString(prefix + '-');
  101. }
  102. return toReturn
  103. }
  104.  
  105. Tag.prototype.push = function(label, href, initialOpen) {
  106. this.addToTree(label.split(this.separator), label, href, initialOpen);
  107. }
  108.  
  109. Tag.prototype.clickExpandTree = function(elem) {
  110. if (elem.html() == '[-]') {
  111. elem.parent().siblings('ol').slideUp();
  112. elem.html('[+]');
  113. }
  114. else
  115. if (elem.html() == '[+]') {
  116. elem.parent().siblings('ol').slideDown();
  117. elem.html('[-]');
  118. }
  119. return false;
  120. }
  121.  
  122.  
  123. var li = $('#sidenav').find('li:has(a[href^="/bookmarks/lookup?"]) , li:has(a[href^="./find?q="])');
  124. li.hide();
  125.  
  126. var tags = null;
  127. li.each(function(index){
  128. var curr = $(this);
  129. var a = curr.find('a');
  130. var bdo = a.find('bdo');
  131. var num = bdo.text();
  132. bdo.remove();
  133. var txt = $.trim(a.text());
  134. if(tags == null)
  135. tags = new Tag(txt, txt, a.attr('href'));
  136. else
  137. tags.push(txt, a.attr('href'), curr.hasClass('selected'));
  138. });
  139. $('#sidenav ul').before( '<ol id="allNestedLabels" class="subLabelsList">' + tags.toLi() + '</ol>');
  140. $('#allNestedLabels .expandLink').click( function() {return Tag.prototype.clickExpandTree( $(this) ); } );
  141. $('#allNestedLabels > li > a').css('color', '#4D90F0');
  142.  
  143. var history = $('#sidenav').find('li:has(a[href^="/history/lookup?"])');
  144. var historyTitle = history.eq(0);
  145. var historyEntries = history.slice(1);
  146. historyEntries.prepend('<div style="margin-left: 17px;" class="expandLinkContainer">|</div>');
  147. historyEntries.hide();
  148. history.find('a').css('display', 'inline');
  149. history.find('a').css('padding-left', '5px');
  150. historyTitle.prepend('<div style="margin-left: 13px;" class="expandLinkContainer"><span class="expandLink">[+]</span></div>');
  151. historyTitle.find('span').click( function() {
  152. var elem = $(this);
  153. if (elem.html() == '[-]') {
  154. historyEntries.slideUp();
  155. elem.html('[+]');
  156. }
  157. else
  158. if (elem.html() == '[+]') {
  159. historyEntries.slideDown();
  160. elem.html('[-]');
  161. }
  162. return false;
  163. });
  164.  
  165.  
  166. //////////////////////////////////////////////////
  167.  
  168. var customCss = new Array();
  169. var i=0;
  170. customCss[i++] = '<style type="text/css">';
  171. customCss[i++] = '.expandLinkContainer{display:inline-block; width:1.5em; text-align:center;}';
  172. customCss[i++] = '.expandLink{cursor:pointer;}';
  173. customCss[i++] = '.subLabelsList{margin-left:1.8em; }';
  174. customCss[i++] = '#allNestedLabels li a {line-height: 18px; display:inline-block;margin:0;padding:0;}';
  175. customCss[i++] = '#allNestedLabels li {white-space:nowrap;}';
  176. customCss[i++] = '#allNestedLabels { margin-left: 1em; margin-bottom: 1em;}';
  177. customCss[i++] = '.selectedLabel{font-weight:bold;}';
  178. customCss[i++] = '</style>'
  179.  
  180. $('head').append(customCss.join('\n'));
  181.