Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  ext-bookmarks.js   Sprache: JAVA

 
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */


"use strict";

ChromeUtils.defineESModuleGetters(this, {
  PlacesUtils: "resource://gre/modules/PlacesUtils.sys.mjs",
});

var { ExtensionError } = ExtensionUtils;

const { TYPE_BOOKMARK, TYPE_FOLDER, TYPE_SEPARATOR } = PlacesUtils.bookmarks;

const BOOKMARKS_TYPES_TO_API_TYPES_MAP = new Map([
  [TYPE_BOOKMARK, "bookmark"],
  [TYPE_FOLDER, "folder"],
  [TYPE_SEPARATOR, "separator"],
]);

const BOOKMARK_SEPERATOR_URL = "data:";

ChromeUtils.defineLazyGetter(this"API_TYPES_TO_BOOKMARKS_TYPES_MAP", () => {
  let theMap = new Map();

  for (let [code, name] of BOOKMARKS_TYPES_TO_API_TYPES_MAP) {
    theMap.set(name, code);
  }
  return theMap;
});

let listenerCount = 0;

function getUrl(type, url) {
  switch (type) {
    case TYPE_BOOKMARK:
      return url;
    case TYPE_SEPARATOR:
      return BOOKMARK_SEPERATOR_URL;
    default:
      return undefined;
  }
}

const getTree = (rootGuid, onlyChildren) => {
  function convert(node, parent) {
    let treenode = {
      id: node.guid,
      title: PlacesUtils.bookmarks.getLocalizedTitle(node) || "",
      index: node.index,
      dateAdded: node.dateAdded / 1000,
      type: BOOKMARKS_TYPES_TO_API_TYPES_MAP.get(node.typeCode),
      url: getUrl(node.typeCode, node.uri),
    };

    if (parent && node.guid != PlacesUtils.bookmarks.rootGuid) {
      treenode.parentId = parent.guid;
    }

    if (node.typeCode == TYPE_FOLDER) {
      treenode.dateGroupModified = node.lastModified / 1000;

      if (!onlyChildren) {
        treenode.children = node.children
          ? node.children.map(child => convert(child, node))
          : [];
      }
    }

    return treenode;
  }

  return PlacesUtils.promiseBookmarksTree(rootGuid)
    .then(root => {
      if (onlyChildren) {
        let children = root.children || [];
        return children.map(child => convert(child, root));
      }
      let treenode = convert(root, null);
      treenode.parentId = root.parentGuid;
      // It seems like the array always just contains the root node.
      return [treenode];
    })
    .catch(e => Promise.reject({ message: e.message }));
};

const convertBookmarks = result => {
  let node = {
    id: result.guid,
    title: PlacesUtils.bookmarks.getLocalizedTitle(result) || "",
    index: result.index,
    dateAdded: result.dateAdded.getTime(),
    type: BOOKMARKS_TYPES_TO_API_TYPES_MAP.get(result.type),
    url: getUrl(result.type, result.url && result.url.href),
  };

  if (result.guid != PlacesUtils.bookmarks.rootGuid) {
    node.parentId = result.parentGuid;
  }

  if (result.type == TYPE_FOLDER) {
    node.dateGroupModified = result.lastModified.getTime();
  }

  return node;
};

const throwIfRootId = id => {
  if (id == PlacesUtils.bookmarks.rootGuid) {
    throw new ExtensionError("The bookmark root cannot be modified");
  }
};

let observer = new (class extends EventEmitter {
  constructor() {
    super();
    this.handlePlacesEvents = this.handlePlacesEvents.bind(this);
  }

  handlePlacesEvents(events) {
    for (let event of events) {
      switch (event.type) {
        case "bookmark-added":
          if (event.isTagging) {
            continue;
          }
          let bookmark = {
            id: event.guid,
            parentId: event.parentGuid,
            index: event.index,
            title: event.title,
            dateAdded: event.dateAdded,
            type: BOOKMARKS_TYPES_TO_API_TYPES_MAP.get(event.itemType),
            url: getUrl(event.itemType, event.url),
          };

          if (event.itemType == TYPE_FOLDER) {
            bookmark.dateGroupModified = bookmark.dateAdded;
          }

          this.emit("created", bookmark);
          break;
        case "bookmark-removed":
          if (event.isTagging || event.isDescendantRemoval) {
            continue;
          }
          let node = {
            id: event.guid,
            parentId: event.parentGuid,
            index: event.index,
            type: BOOKMARKS_TYPES_TO_API_TYPES_MAP.get(event.itemType),
            url: getUrl(event.itemType, event.url),
            title: event.title,
          };

          this.emit("removed", {
            guid: event.guid,
            info: { parentId: event.parentGuid, index: event.index, node },
          });
          break;
        case "bookmark-moved":
          this.emit("moved", {
            guid: event.guid,
            info: {
              parentId: event.parentGuid,
              index: event.index,
              oldParentId: event.oldParentGuid,
              oldIndex: event.oldIndex,
            },
          });
          break;
        case "bookmark-title-changed":
          if (event.isTagging) {
            continue;
          }

          this.emit("changed", {
            guid: event.guid,
            info: { title: event.title },
          });
          break;
        case "bookmark-url-changed":
          if (event.isTagging) {
            continue;
          }

          this.emit("changed", {
            guid: event.guid,
            info: { url: event.url },
          });
          break;
      }
    }
  }
})();

const decrementListeners = () => {
  listenerCount -= 1;
  if (!listenerCount) {
    PlacesUtils.observers.removeListener(
      [
        "bookmark-added",
        "bookmark-removed",
        "bookmark-moved",
        "bookmark-title-changed",
        "bookmark-url-changed",
      ],
      observer.handlePlacesEvents
    );
  }
};

const incrementListeners = () => {
  listenerCount++;
  if (listenerCount == 1) {
    PlacesUtils.observers.addListener(
      [
        "bookmark-added",
        "bookmark-removed",
        "bookmark-moved",
        "bookmark-title-changed",
        "bookmark-url-changed",
      ],
      observer.handlePlacesEvents
    );
  }
};

this.bookmarks = class extends ExtensionAPIPersistent {
  PERSISTENT_EVENTS = {
    onCreated({ fire }) {
      let listener = (event, bookmark) => {
        fire.sync(bookmark.id, bookmark);
      };

      observer.on("created", listener);
      incrementListeners();
      return {
        unregister() {
          observer.off("created", listener);
          decrementListeners();
        },
        convert(_fire) {
          fire = _fire;
        },
      };
    },

    onRemoved({ fire }) {
      let listener = (event, data) => {
        fire.sync(data.guid, data.info);
      };

      observer.on("removed", listener);
      incrementListeners();
      return {
        unregister() {
          observer.off("removed", listener);
          decrementListeners();
        },
        convert(_fire) {
          fire = _fire;
        },
      };
    },

    onChanged({ fire }) {
      let listener = (event, data) => {
        fire.sync(data.guid, data.info);
      };

      observer.on("changed", listener);
      incrementListeners();
      return {
        unregister() {
          observer.off("changed", listener);
          decrementListeners();
        },
        convert(_fire) {
          fire = _fire;
        },
      };
    },

    onMoved({ fire }) {
      let listener = (event, data) => {
        fire.sync(data.guid, data.info);
      };

      observer.on("moved", listener);
      incrementListeners();
      return {
        unregister() {
          observer.off("moved", listener);
          decrementListeners();
        },
        convert(_fire) {
          fire = _fire;
        },
      };
    },
  };

  getAPI(context) {
    return {
      bookmarks: {
        async get(idOrIdList) {
          let list = Array.isArray(idOrIdList) ? idOrIdList : [idOrIdList];

          try {
            let bookmarks = [];
            for (let id of list) {
              let bookmark = await PlacesUtils.bookmarks.fetch({ guid: id });
              if (!bookmark) {
                throw new Error("Bookmark not found");
              }
              bookmarks.push(convertBookmarks(bookmark));
            }
            return bookmarks;
          } catch (error) {
            return Promise.reject({ message: error.message });
          }
        },

        getChildren: function (id) {
          // TODO: We should optimize this.
          return getTree(id, true);
        },

        getTree: function () {
          return getTree(PlacesUtils.bookmarks.rootGuid, false);
        },

        getSubTree: function (id) {
          return getTree(id, false);
        },

        search: function (query) {
          return PlacesUtils.bookmarks
            .search(query)
            .then(result => result.map(convertBookmarks));
        },

        getRecent: function (numberOfItems) {
          return PlacesUtils.bookmarks
            .getRecent(numberOfItems)
            .then(result => result.map(convertBookmarks));
        },

        create: function (bookmark) {
          let info = {
            title: bookmark.title || "",
          };

          info.type = API_TYPES_TO_BOOKMARKS_TYPES_MAP.get(bookmark.type);
          if (!info.type) {
            // If url is NULL or missing, it will be a folder.
            if (bookmark.url !== null) {
              info.type = TYPE_BOOKMARK;
            } else {
              info.type = TYPE_FOLDER;
            }
          }

          if (info.type === TYPE_BOOKMARK) {
            info.url = bookmark.url || "";
          }

          if (bookmark.index !== null) {
            info.index = bookmark.index;
          }

          if (bookmark.parentId !== null) {
            throwIfRootId(bookmark.parentId);
            info.parentGuid = bookmark.parentId;
          } else {
            info.parentGuid = PlacesUtils.bookmarks.unfiledGuid;
          }

          try {
            return PlacesUtils.bookmarks
              .insert(info)
              .then(convertBookmarks)
              .catch(error => Promise.reject({ message: error.message }));
          } catch (e) {
            return Promise.reject({
              message: `Invalid bookmark: ${JSON.stringify(info)}`,
            });
          }
        },

        move: function (id, destination) {
          throwIfRootId(id);
          let info = {
            guid: id,
          };

          if (destination.parentId !== null) {
            throwIfRootId(destination.parentId);
            info.parentGuid = destination.parentId;
          }
          info.index =
            destination.index === null
              ? PlacesUtils.bookmarks.DEFAULT_INDEX
              : destination.index;

          try {
            return PlacesUtils.bookmarks
              .update(info)
              .then(convertBookmarks)
              .catch(error => Promise.reject({ message: error.message }));
          } catch (e) {
            return Promise.reject({
              message: `Invalid bookmark: ${JSON.stringify(info)}`,
            });
          }
        },

        update: function (id, changes) {
          throwIfRootId(id);
          let info = {
            guid: id,
          };

          if (changes.title !== null) {
            info.title = changes.title;
          }
          if (changes.url !== null) {
            info.url = changes.url;
          }

          try {
            return PlacesUtils.bookmarks
              .update(info)
              .then(convertBookmarks)
              .catch(error => Promise.reject({ message: error.message }));
          } catch (e) {
            return Promise.reject({
              message: `Invalid bookmark: ${JSON.stringify(info)}`,
            });
          }
        },

        remove: function (id) {
          throwIfRootId(id);
          let info = {
            guid: id,
          };

          // The API doesn't give you the old bookmark at the moment
          try {
            return PlacesUtils.bookmarks
              .remove(info, { preventRemovalOfNonEmptyFolders: true })
              .catch(error => Promise.reject({ message: error.message }));
          } catch (e) {
            return Promise.reject({
              message: `Invalid bookmark: ${JSON.stringify(info)}`,
            });
          }
        },

        removeTree: function (id) {
          throwIfRootId(id);
          let info = {
            guid: id,
          };

          try {
            return PlacesUtils.bookmarks
              .remove(info)
              .catch(error => Promise.reject({ message: error.message }));
          } catch (e) {
            return Promise.reject({
              message: `Invalid bookmark: ${JSON.stringify(info)}`,
            });
          }
        },

        onCreated: new EventManager({
          context,
          module: "bookmarks",
          event: "onCreated",
          extensionApi: this,
        }).api(),

        onRemoved: new EventManager({
          context,
          module: "bookmarks",
          event: "onRemoved",
          extensionApi: this,
        }).api(),

        onChanged: new EventManager({
          context,
          module: "bookmarks",
          event: "onChanged",
          extensionApi: this,
        }).api(),

        onMoved: new EventManager({
          context,
          module: "bookmarks",
          event: "onMoved",
          extensionApi: this,
        }).api(),
      },
    };
  }
};

Messung V0.5
C=94 H=97 G=95

¤ Dauer der Verarbeitung: 0.2 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge