Сортировка закладок по типу, названию и домену( Firefox 12 + )
Автор: bunda1, Dumby, hydrolizer
Версия: от 05.07.2014.
Описание: Kод добавляет в контекстное меню папок закладок новый пункт Сортировать по типу, названию и домену который сортирует закладки по типу, названию и домену. Код при сортировке удаляет разделители, а подпапки закладок если такие имеются отсортирование по названию перемещает в начало папки закладок, за подпапками ставит обычные закладки отсортирование по названию, а за ними закладки по доменным группам и каждая группа отсортирована по названию. После сортировки порядок папке будет вот такой:
1. Папки, отсортирование по названию
2. Закладки без одинаковых доменов, отсортирование по названию,
3. Закладки по доменным группам, каждая группа отсортирована по названию.

При сортировке по названию порядок сортировки вот такой: цифры --> английский --> кириллица.

Пример сортировки:
c97aa07411c0595f2661ae01c7af1ff3.jpg    248040550226daabc31d7780b65a4edd.jpg

Использование: положите код в любую Custom Buttons кнопку, в инициализацию. Не обязательно создавать новую CB кнопку, можно использовать уже существующую.

Выделить код

Код:

// Добавить новый пункт "Сортировать по типу, названию и домену" в меню папок закладок, от 05.07.2014. ................................
(function() {
   var menuitem = document.createElement("menuitem");
   menuitem.id = "placesContext_sortBy:locationAndName";
   menuitem.setAttribute("label", "Сортировать по домену и названию");
   menuitem.setAttribute("selection", "folder");
   menuitem.setAttribute("closemenu", "single");   
  
   menuitem.setAttribute("oncommand", "this.run(document)"); // если клик или команда на пункте меню
   menuitem.run = function(doc) {
      // получить Id и контент папки закладок  
      var node = PlacesUIUtils.getViewForNode( doc.popupNode ).selectedNode;
      var folderId = node.folderItemId ? node.folderItemId : node.itemId;
      var contents = PlacesUtils.getFolderContents( folderId, false, false ).root;
      
      // удалить разделители из папки закладок, создать массив с закладками и массив c подпапками закладок 
      for ( var primaryList = [], foldersList = [], i = 0; i < contents.childCount; ++i ) {
            var item = contents.getChild(i);
            item.type == 7 
            ? (PlacesUtils.bookmarks.removeItem( item.itemId ), i--) // удалить разделители
            : (!item.type ? primaryList : foldersList).push( item );  // заполнить массивы
            }
      
      // создать массив с закладками у которых есть одинаковые домены и массив c обычными закладками
      primaryList.forEach(function(item) {
         var domain = item.uri.split(/\/+/g)[1];
         primaryList[domain] = domain in primaryList;
      });
      var domainList = [], placesList = [];
      primaryList.forEach(function(item) 
         ( primaryList[item.uri.split(/\/+/g)[1]] ? domainList : placesList ).push(item)
      );

      // создать функцию сортировки
      var sort = function(arr, prop, mod) {
         mod = mod || function(arg) arg;
         arr.sort(function(a, b) mod(a[prop]).localeCompare(mod(b[prop]), "en"));
      } 

      // отсортировать все массивы по названию и массив с одинаковыми доменам по доменам
      sort( foldersList, "title");
      sort( placesList, "title");
      sort( domainList, "title");
      sort( domainList, "uri", function(uri) uri.split(/\/+/g)[1] );
      
      // соединить в нужном порядке все массивы в новом массиве
      var newPlacesList = foldersList.concat( placesList ).concat( domainList );
       
      // изменить меню закладок в соответствии с новым массивом 
      let callback = {
             runBatched: function() {
                for( let i = 0; i < newPlacesList.length; ++i )
                     PlacesUtils.bookmarks.setItemIndex( newPlacesList[i].itemId, i );
             }
      }
      PlacesUtils.bookmarks.runInBatchMode( callback, null ); 
   };
   
  
   // добавить новый пункт во все меню папок закладок ....
   function handlePopup(e) {     
      var node = e.target;
      if ( node.id !== 'placesContext' ) return;
      
      var sortByName = node.getElementsByAttribute("id", "placesContext_sortBy:name")[0];    
      setTimeout(function() { menuitem.setAttribute("disabled", sortByName.disabled ) }, 50 ); // отключать, если 'Сортировать по имени' отключено 
      
      if ( node.getElementsByAttribute("id", "placesContext_sortBy:locationAndName")[0] ) return; // блокировать дублирование    
      node.insertBefore( menuitem, sortByName );            
   };     
   addEventListener("popupshowing", handlePopup, true, window );
   addDestructor(function() menuitem.parentNode && menuitem.parentNode.removeChild(menuitem) );
      

   // Добавить новый пункт во все меню папок закладок библиотеки ....
   function winObs( subject ) {  
   
      subject.addEventListener("load", function(e) {
         this.removeEventListener( e.type, arguments.callee );         
         
         if ( subject.location.href !== 'chrome://browser/content/places/places.xul' ) return; // стоп, если не библиотека        

         // добавлять и удалять обработчик для добавления в меню папок закладок новый пункт
         addEventListener("popupshowing", handlePopup, true, subject );          
         this.addEventListener("unload", function(e) {
             this.removeEventListener(e.type, arguments.callee );
             removeEventListener("popupshowing", handlePopup, true, subject );
         })  
      })
   };
   Services.ww.registerNotification(winObs);
   addDestructor(function() { Services.ww.unregisterNotification(winObs) });
   
})();

Странно. Почему-то у меня в кнопке popupshowing, повешенный на placesContext, отрабатывает только в случае вызова контекстного меню на папке панели закладок; в сайдбаре и в управлении закладками этот листенер просто не срабатывает. В расширении, из которого код для кнопки, листенер вешается в onLoad() главного окна, и отрабатывает в любом месте вызова. В чем разница - так и не понял.

hydrolizer
Я попробовал без обработчика на placesContext и вроде все работает, может без него можно ?

bunda1
Ну, в общем можно. Нужно только в атрибуты для пункта меню добавить selection="folder" (чтобы пункт появлялся только при вызове на папке). Там просто делаются доп. проверки на то, что папка не read-only (бывают такие моменты, но это состояние есть только на уровне UI, т.е. операции вполне могут выполняться, и было бы неправильным, если бы можно было изменять содержимое read-only папки), и что содержимое не имеет доп сортировки (а это вроде бы всегда так и есть).

hydrolizer
Ok.
menuitem.setAttribute("selection", "folder");

Что-то я не совсем понял как ей пользоваться. Можно попродробней?

k1net1k
ПКМ на папке с закладками - в управлении закладками или в сайдбаре - "Сортировать по типу, домену и названию". На саму кнопку нажимать не надо, её вообще можно спрятать, т.к. весь её нужный код в инициализации.

Нажал добавить кнопку -> положил верхний код в инициализацию -> нажал "Ок"
Нажал закладки -> ПКМ по папке -> контекстное меню не открывается
Зашел в "Показать все закладки" -> ПКМ по папке там есть только "Сортировать по имени"

ЧЯДНТ?

k1net1k пишет

ЧЯДНТ?

Перезагружаться пробовал?

k1net1k
по ходу ты кнопку создал, а на панель ее не вытащил!
вот ничего и не получается

Inko7
Вытащил ума хватило =)

voqabuhe
Пробовал

k1net1k пишет

ЧЯДНТ?

Всё так. Просто пока кнопка не готова.
bunda1
Я понял, в чем дело. У меня в расширении пункт меню внедряется так: в chrome.manifest указано

overlay        chrome://browser/content/places/placesOverlay.xul    chrome://uithings/content/places-overlay.xul

далее в chrome://uithings/content/places-overlay.xul

Выделить код

Код:

<overlay id="places-overlay" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
  <script type="application/javascript" src="chrome://uithings/content/sortplaces.js"/>

  <menupopup id="placesContext">
    <menuitem id="uithings-places-sort" label="&uithings.placessort.menuitem;" closemenu="single" selection="folder"
      insertafter="placesContext_sortBy:name" oncommand="uithings.placessort.sort()"/>
  </menupopup>
</overlay>

Тут нюанс в том, что chrome://browser/content/places/placesOverlay.xul, как и chrome://uithings/content/places-overlay.xul (это оверлей моего расширения) - не DOM-документы, это именно оверлеи. Из этих оверлеев конструируются документы при запуске браузера и загрузке расширений - происходит слияние оверлеев (подробнее прочитать можно, например, здесь). Поскольку у меня пункт меню уже жестко вписан в оверлей, то он в нем и находится, и при слиянии оверлеев копируется во все документы, структура которых сливается с оверлеем places. Поэтому пункт из моего расширения есть везде, где используется placesContext. В случае кнопки мы ищем placesContext по document.getElementById; т.к. документ у нас - главное окно, то и находим то, что в нем есть - placesContext панели закладок (и меню закладок). Управление закладками и сайдбар - это совсем другие документы, поэтому в их placesContext наш пункт меню просто не попадает. Нужно думать, как внедриться в них - на момент старта браузера мы в общем случае к ним не сможем получить доступа, т.к. сайдбар будет закрыт, как и управление закладками.

hydrolizer
Понятно и жаль, может позже придут решение, вот сейчас я припоминаю что некоторые CB коды что то добавляют в сайдбар, правда не в контекстное меню папки закладок а на sidebar-header. Но не все так плохо -  лично я практически не пользуюсь управлением закладками и сайдбаром.
К слову, как узнать название папки закладок на панели закладок, хочу сделать добавление текучей страницы в папку закладок двойным кликом на папке:

Выделить код

Код:

window.addEventListener("dblclick", function(event){
if(event.button !== 0) return;
var target = event.originalTarget;
if ( target.localName == "toolbarbutton" && !!target._placesNode && !PlacesUtils.nodeIsURI(target._placesNode) ) {
alert(?????)
}
 },true);
bunda1 пишет

К слову, как узнать название папки закладок на панели закладок

Тут нужно не название, а id этой папки. Примерно так:

Выделить код

Код:

document.getElementById("PlacesToolbar").addEventListener("dblclick", function(event)
{
  if(event.button !== 0) return;
  var target = event.originalTarget;
  Cu.import("resource://gre/modules/PlacesUtils.jsm");
  if (!(target.localName == "toolbarbutton" && !!target._placesNode
    && PlacesUtils.nodeIsFolder(target._placesNode) && !PlacesUtils.nodeIsReadOnly(target._placesNode)))
      return;
  var folderId=PlacesUtils.getConcreteItemId(target._placesNode);
  var currentDocURI = Services.io.newURI(gBrowser.contentDocument.location.href,null,null);
  let callback = {
    runBatched: function() {
      PlacesUtils.bookmarks.insertBookmark(folderId, currentDocURI, Ci.nsINavBookmarksService.DEFAULT_INDEX, null);
    }
  }
  PlacesUtils.bookmarks.runInBatchMode(callback, null);
  Services.console.logStringMessage("bookmark created");
}, false);

hydrolizer
Большое спасибо, очень полезный код.
Все работает, но есть одна непонятка если открыть свойства добавленной закладки:
7b7f42bb0508.jpg

bunda1 пишет

Все работает, но есть одна непонятка если открыть свойства добавленной закладки:

В PlacesUtils.bookmarks.insertBookmark последним параметром передавайте не null, а gBrowser.contentTitle.

hydrolizer
Большое спасибо :) .

Эм теперь то можно сортировать по домену или нет? =)

k1net1k пишет

Эм теперь то можно сортировать по домену или нет? =)

Можно, но нужный пункт меню есть только в папках закладок на панели закладок .

bunda1 пишет

Можно, но нужный пункт меню есть только в папках закладок на панели закладок .

Должен быть еще в меню закладок (т.е. контекстное меню пунктов меню закладок).

hydrolizer пишет

Должен быть еще в меню закладок (т.е. контекстное меню пунктов меню закладок).

Нету, и мне кажется не должно быть, потому что в контекстном меню пунктов меню закладок нету пункта Сортировать по имени:
SortByName = document.getElementById("placesContext_sortBy:name")
SortByName.parentNode.insertBefore(menuitem, SortByName );

bunda1
Вот скриншот с нового чистого профиля - установлены только CB:

скрытый текст
places-menu-ctx.png

Да, кстати: чтобы при вызове операции таким вот образом - из контекстного меню на пункте другого меню - не схлопывалось основное меню, а только контекстное, для menuitem нужен еще один атрибут: closemenu="single".

hydrolizer
Да я ошибся, я добавлю это в описание кода.

Да, кстати: чтобы при вызове операции таким вот образом - из контекстного меню на пункте другого меню - не схлопывалось основное меню, а только контекстное, для menuitem нужен еще один атрибут: closemenu="single".

Ok.

Очень хорошая кнопка, я правда и не думал все закладки сваливать в одну папку, но всё же
Подскажите как убрать этот момент

скрытый текст
2cv0

LongLogin пишет

Подскажите как убрать этот момент

Сегодня 12:49:01

:sick: Сколько не пытаюсь создать эту кнопку все равно нечего невыходит - даже на новом профиле.

k1net1k
Что именно не выходит? Могу еще раз напомнить: пункт меню будет только в контекстном меню панели закладок, и в контестном меню пунктов выпадающего меню закладок (это то, которое вызывается из рыжей кнопки (например)).
В сайдбаре и в управлении закладками его не будет - кажется, это та задача, которую с помощью CB нельзя решить в принципе.

hydrolizer
Хочу сделать удаление закладок из папки закладок на панели закладок и из меню закладок двойным правым кликом помоги пожалуйста с кодом:

Выделить код

Код:

window.addEventListener("mouseup", function(event) {
          var target = event.originalTarget;
          if ( event.button == 2 && target.localName == "menuitem" && target._placesNode && PlacesUtils.nodeIsURI(target._placesNode) ){
          var itemId = ?????
          PlacesUtils.bookmarks.removeItem(itemId); 
          }
}, false);

Двойной правый клик потом приделаю с таймером а то "dblclick" не работает на закладках.

bunda1
Объясни мне сначала точнее предполагаемый юзкейс, а то я, честно говоря, не особенно понял его суть. Ты хочешь: а) удалять закладки из папки, расположенной на панели закладок, двойным ПКМ по этой папке; б) удалять закладки из папки меню закладок двойным ПКМ по элементу меню, который представляет из себя папку с закладками, так? И отдельно - необходимо вычистить вообще всю папку, или только закладки на первом уровне иерархии, не затрагивая закладки во вложенных папках?

bunda1 пишет

Сортировка закладок по типу, домену и названию(Firefox 12 +)Автор: hydrolizer

Не совсем понятно кого спрашивать? Автор: hydrolizer, а публикует bunda1 ? :)

Но есть вопросы.
1. Это возможно сделать только для ФФ >= 12 или для esr 10 тоже возможно? Меня более интересует esr версия..

2. Существующая стандартная сортировка по имени меня вполне устраивает и польза от нее очевидна - удаление дубликатов.
А вот зачем нужны другие сортировки?

3. Что я бы хотел иметь и использовать?
Я бы хотел чтобы после переключения сортировки на стандартную сортировку по имени и удаления дубликатов была возможность вернуть все назад - т.е. - вернуть отсутствие сортировки. Это имеет смысл. Все остальное мало интересно и надумано. Те3м более - нарушение местоположения сепараторов. Без обид.
Возможно ли такое запоминание или, по другому, - сброс сортировки? Стандартно я такой опции не вижу, а она имела бы смысл и была бы очень полезна

LBra пишет

Я бы хотел [...] удаления дубликатов

BookmarkDD

LongLogin
Спасибо, но я предпочитаю удалять вручную. Кроме того, я ведь не об этом спрашивал.

Эээ... а почему бы не открыть управление закладками? Там же и поиск есть, и сортировка. Можно вбить в поиск «:» и отсортировать результаты.

Infocatcher пишет

Эээ... а почему бы не открыть управление закладками? Там же и поиск есть, и сортировка. Можно вбить в поиск «:» и отсортировать результаты.

Да, действительно. Спасибо Infocatcher. :beer:
Все вопросы сняты. Бесполезная кнопка. (Для меня, конечно).
Управлять закладками как и историей нужно с помощью менеджера, тогда упорядочению записей ничто и никто угрожать не будет!
:D

21-06-2012 18:45:25

bunda1 пишет

Открой "Показать все закладки" + "Сортировка"+ "Сортировка по дате добавления", и все дела.

Да нет, это не так. Так будет только в том случае если ты не упорядочивал записи после добавления, накидал их в кучу и так оставил... а если нет - то нужно выбирать "Не сортировать"... я не проверял, правда... и скорее всего эта опция касается только менеджера, но не всех других случаев.
Впрочем, Infocatcher уже расставил все точки над i.
Не трогать сортировку и все будет хоккей! :)

LBra пишет

я не проверял, правда... и скорее всего эта опция касается только менеджера

Проверил. Так и есть, если ты уже нарушил сортировку, то ее с помощью Менеджера закладок не вернешь. Точнее - не вернешь вообще.

21-06-2012 19:02:24
LongLogin
Вопрос уже ясен как белый день.
Я не сохраняю закладки охапками по сто штук за раз, никаких "зако.." не будет если нужно удалить парочку лишних записей в одной папке.
А тупо удалять дубликат, который находится в другой папке, спасибо, не нада. Может он там специально подшит?

LBra пишет

не будет если нужно удалить парочку лишних записей в одной папке

да, но каким образом они там смогут образоваться если человек не страдает альц-геймером или рассеянным склерозом

LBra пишет

тогда упорядочению записей ничто и никто угрожать не будет!

Если что: менеджер сортирует только по имени, кнопка - сначала по домену, потом по имени.

hydrolizer
Мне просто это не нужно.. :)

Но за эту тему спасибо, заставила задуматься... убрал сейчас у себя и ту стандартную сортировку из контекстного меню (пункт меню и сепаратор) чтобы не сбивала с толку.
если что:

Выделить код

Код:

document.getElementById("placesContext_sortBy:name").collapsed = true;
    document.getElementById("placesContext_sortSeparator").collapsed = true;

;)

hydrolizer пишет

..... Ты хочешь: а) удалять закладки из папки, расположенной на панели закладок, двойным ПКМ по этой папке; б) удалять закладки из папки меню закладок двойным ПКМ по элементу меню, который представляет из себя папку с закладками, так? И отдельно - необходимо вычистить вообще всю папку, или только закладки на первом уровне иерархии, не затрагивая закладки во вложенных папках?

Извини, я имел ввиду удаление одной закладки(не папки) на которой будет происходить двойной клик, чтобы не открывать контекстное меню закладки и не нажимать "Удалить"

скрытый текст
2a72542bbb14.jpg

bunda1
Тут вскрылся баг: при попытке сортировки на корневых системных папках (Меню закладок, Панель закладок, неподшитые закладки) никакой сортировки просто не происходит. Причина в том, что эти папки на уровне интерфейса являются не именно корневыми папками, а папками поиска, непосредственно в себе ничего не содержащими. Чтобы сортировка работала на таких папках, после определения folderId (var folderId=view.selectedNode.itemId) нужно провести небольшое уточнение:

Выделить код

Код:

var match = view.selectedNode.uri.match(/^place\:folder=(\w+)/);
var bs=Cc["@mozilla.org/browser/nav-bookmarks-service;1"].getService(Ci.nsINavBookmarksService);
if (match)
  switch(match[1])
  {
    case "BOOKMARKS_MENU":
      folderId=bs.bookmarksMenuFolder;
      break;
    case "TOOLBAR":
      folderId=bs.toolbarFolder;
      break;
    case "UNFILED_BOOKMARKS":
      folderId=bs.unfiledBookmarksFolder;
      break;
  }

Я исправил: 23-05-2012 19:46:25

Infocatcher пишет

Эээ... а почему бы не открыть управление закладками? Там же и поиск есть, и сортировка. Можно вбить в поиск «:» и отсортировать результаты.

А можно как-то добавить поиск по полю Description?