Source: jsxc.lib.xmpp.bookmarks.js

/**
 * Load and save bookmarks according to XEP-0048.
 *
 * @namespace jsxc.xmpp.bookmarks
 */
jsxc.xmpp.bookmarks = {};

/**
 * Determines if server is able to store bookmarks.
 *
 * @return {boolean} True: Server supports bookmark storage
 */
jsxc.xmpp.bookmarks.remote = function() {
   return jsxc.xmpp.conn.caps && jsxc.xmpp.hasFeatureByJid(jsxc.xmpp.conn.domain, Strophe.NS.PUBSUB + "#publish");
};

/**
 * Load bookmarks from pubsub.
 *
 * @memberOf jsxc.xmpp.bookmarks
 */
jsxc.xmpp.bookmarks.load = function() {
   var caps = jsxc.xmpp.conn.caps;
   var ver = caps._jidVerIndex[jsxc.xmpp.conn.domain];

   if (!ver || !caps._knownCapabilities[ver]) {
      // wait until we know server capabilities
      $(document).on('caps.strophe', function(ev, from) {
         if (from === jsxc.xmpp.conn.domain) {
            jsxc.xmpp.bookmarks.load();

            $(document).off(ev);
         }
      });
   }

   if (jsxc.xmpp.bookmarks.remote()) {
      jsxc.xmpp.bookmarks.loadFromRemote();
   } else {
      jsxc.xmpp.bookmarks.loadFromLocal();
   }
};

/**
 * Load bookmarks from local storage.
 *
 * @private
 */
jsxc.xmpp.bookmarks.loadFromLocal = function() {
   jsxc.debug('Load bookmarks from local storage');

   var bookmarks = jsxc.storage.getUserItem('bookmarks') || [];
   var bl = jsxc.storage.getUserItem('buddylist') || [];

   $.each(bookmarks, function() {
      var room = this;
      var roomdata = jsxc.storage.getUserItem('buddy', room) || {};

      bl.push(room);
      jsxc.gui.roster.add(room);

      if (roomdata.autojoin) {
         jsxc.debug('auto join ' + room);
         jsxc.xmpp.conn.muc.join(room, roomdata.nickname);
      }
   });

   jsxc.storage.setUserItem('buddylist', bl);
};

/**
 * Load bookmarks from remote storage.
 *
 * @private
 */
jsxc.xmpp.bookmarks.loadFromRemote = function() {
   jsxc.debug('Load bookmarks from pubsub');

   var bookmarks = jsxc.xmpp.conn.bookmarks;

   bookmarks.get(function(stanza) {
      var bl = jsxc.storage.getUserItem('buddylist');

      $(stanza).find('conference').each(function() {
         var conference = $(this);
         var room = conference.attr('jid');
         var roomName = conference.attr('name') || room;
         var autojoin = conference.attr('autojoin') || false;
         var nickname = conference.find('nick').text();
         nickname = (nickname.length > 0) ? nickname : Strophe.getNodeFromJid(jsxc.xmpp.conn.jid);

         if (autojoin === 'true') {
            autojoin = true;
         } else if (autojoin === 'false') {
            autojoin = false;
         }

         var data = jsxc.storage.getUserItem('buddy', room) || {};

         data = $.extend(data, {
            jid: room,
            name: roomName,
            sub: 'both',
            status: 0,
            type: 'groupchat',
            state: jsxc.muc.CONST.ROOMSTATE.INIT,
            subject: null,
            bookmarked: true,
            autojoin: autojoin,
            nickname: nickname
         });

         jsxc.storage.setUserItem('buddy', room, data);

         bl.push(room);
         jsxc.gui.roster.add(room);

         if (autojoin) {
            jsxc.debug('auto join ' + room);
            jsxc.xmpp.conn.muc.join(room, nickname);
         }
      });

      jsxc.storage.setUserItem('buddylist', bl);
   }, function(stanza) {
      var err = jsxc.xmpp.bookmarks.parseErr(stanza);

      if (err.reasons[0] === 'item-not-found') {
         jsxc.debug('create bookmark node');

         bookmarks.createBookmarksNode(function() {
            jsxc.debug('Bookmark node created.');
         }, function() {
            jsxc.debug('Could not create bookmark node.');
         });
      } else {
         jsxc.debug('[XMPP] Could not create bookmark: ' + err.type, err.reasons);
      }
   });
};

/**
 * Parse received error.
 *
 * @param  {string} stanza
 * @return {object} err - The parsed error
 * @return {string} err.type - XMPP error type
 * @return {array} err.reasons - Array of error reasons
 */
jsxc.xmpp.bookmarks.parseErr = function(stanza) {
   var error = $(stanza).find('error');
   var type = error.attr('type');
   var reasons = error.children().map(function() {
      return $(this).prop('tagName');
   });

   return {
      type: type,
      reasons: reasons
   };
};

/**
 * Deletes the bookmark for the given room and removes it from the roster if soft is false.
 *
 * @param  {string} room - room jid
 * @param  {boolean} [soft=false] - True: leave room in roster
 */
jsxc.xmpp.bookmarks.delete = function(room, soft) {

   if (!soft) {
      jsxc.gui.roster.purge(room);
   }

   if (jsxc.xmpp.bookmarks.remote()) {
      jsxc.xmpp.bookmarks.deleteFromRemote(room, soft);
   } else {
      jsxc.xmpp.bookmarks.deleteFromLocal(room, soft);
   }
};

/**
 * Delete bookmark from remote storage.
 *
 * @private
 * @param  {string} room - room jid
 * @param  {boolean} [soft=false] - True: leave room in roster
 */
jsxc.xmpp.bookmarks.deleteFromRemote = function(room, soft) {
   var bookmarks = jsxc.xmpp.conn.bookmarks;

   bookmarks.delete(room, function() {
      jsxc.debug('Bookmark deleted ' + room);

      if (soft) {
         jsxc.gui.roster.getItem(room).removeClass('jsxc_bookmarked');
         jsxc.storage.updateUserItem('buddy', room, 'bookmarked', false);
         jsxc.storage.updateUserItem('buddy', room, 'autojoin', false);
      }
   }, function(stanza) {
      var err = jsxc.xmpp.bookmarks.parseErr(stanza);

      jsxc.debug('[XMPP] Could not delete bookmark: ' + err.type, err.reasons);
   });
};

/**
 * Delete bookmark from local storage.
 *
 * @private
 * @param  {string} room - room jid
 * @param  {boolean} [soft=false] - True: leave room in roster
 */
jsxc.xmpp.bookmarks.deleteFromLocal = function(room, soft) {
   var bookmarks = jsxc.storage.getUserItem('bookmarks');
   var index = bookmarks.indexOf(room);

   if (index > -1) {
      bookmarks.splice(index, 1);
   }

   jsxc.storage.setUserItem('bookmarks', bookmarks);

   if (soft) {
      jsxc.gui.roster.getItem(room).removeClass('jsxc_bookmarked');
      jsxc.storage.updateUserItem('buddy', room, 'bookmarked', false);
      jsxc.storage.updateUserItem('buddy', room, 'autojoin', false);
   }
};

/**
 * Adds or overwrites bookmark for given room.
 *
 * @param  {string} room - room jid
 * @param  {string} alias - room alias
 * @param  {string} nick - preferred user nickname
 * @param  {boolean} autojoin - should we join this room after login?
 */
jsxc.xmpp.bookmarks.add = function(room, alias, nick, autojoin) {
   if (jsxc.xmpp.bookmarks.remote()) {
      jsxc.xmpp.bookmarks.addToRemote(room, alias, nick, autojoin);
   } else {
      jsxc.xmpp.bookmarks.addToLocal(room, alias, nick, autojoin);
   }
};

/**
 * Adds or overwrites bookmark for given room in remote storage.
 *
 * @private
 * @param  {string} room - room jid
 * @param  {string} alias - room alias
 * @param  {string} nick - preferred user nickname
 * @param  {boolean} autojoin - should we join this room after login?
 */
jsxc.xmpp.bookmarks.addToRemote = function(room, alias, nick, autojoin) {
   var bookmarks = jsxc.xmpp.conn.bookmarks;

   var success = function() {
      jsxc.debug('New bookmark created', room);

      jsxc.gui.roster.getItem(room).addClass('jsxc_bookmarked');
      jsxc.storage.updateUserItem('buddy', room, 'bookmarked', true);
      jsxc.storage.updateUserItem('buddy', room, 'autojoin', autojoin);
      jsxc.storage.updateUserItem('buddy', room, 'nickname', nick);
   };
   var error = function() {
      jsxc.warn('Could not create bookmark', room);
   };

   bookmarks.add(room, alias, nick, autojoin, success, error);
};

/**
 * Adds or overwrites bookmark for given room in local storage.
 *
 * @private
 * @param  {string} room - room jid
 * @param  {string} alias - room alias
 * @param  {string} nick - preferred user nickname
 * @param  {boolean} autojoin - should we join this room after login?
 */
jsxc.xmpp.bookmarks.addToLocal = function(room, alias, nick, autojoin) {
   jsxc.gui.roster.getItem(room).addClass('jsxc_bookmarked');
   jsxc.storage.updateUserItem('buddy', room, 'bookmarked', true);
   jsxc.storage.updateUserItem('buddy', room, 'autojoin', autojoin);
   jsxc.storage.updateUserItem('buddy', room, 'nickname', nick);

   var bookmarks = jsxc.storage.getUserItem('bookmarks') || [];

   if (bookmarks.indexOf(room) < 0) {
      bookmarks.push(room);

      jsxc.storage.setUserItem('bookmarks', bookmarks);
   }
};

/**
 * Show dialog to edit bookmark.
 *
 * @param  {string} room - room jid
 */
jsxc.xmpp.bookmarks.showDialog = function(room) {
   var dialog = jsxc.gui.dialog.open(jsxc.gui.template.get('bookmarkDialog'));
   var data = jsxc.storage.getUserItem('buddy', room);

   $('#jsxc_room').val(room);
   $('#jsxc_nickname').val(data.nickname);

   $('#jsxc_bookmark').change(function() {
      if ($(this).prop('checked')) {
         $('#jsxc_nickname').prop('disabled', false);
         $('#jsxc_autojoin').prop('disabled', false);
         $('#jsxc_autojoin').parent('.checkbox').removeClass('disabled');
      } else {
         $('#jsxc_nickname').prop('disabled', true);
         $('#jsxc_autojoin').prop('disabled', true).prop('checked', false);
         $('#jsxc_autojoin').parent('.checkbox').addClass('disabled');
      }
   });

   $('#jsxc_bookmark').prop('checked', data.bookmarked);
   $('#jsxc_autojoin').prop('checked', data.autojoin);

   $('#jsxc_bookmark').change();

   dialog.find('form').submit(function(ev) {
      ev.preventDefault();

      var bookmarked = $('#jsxc_bookmark').prop('checked');
      var autojoin = $('#jsxc_autojoin').prop('checked');
      var nickname = $('#jsxc_nickname').val();

      if (bookmarked) {
         jsxc.xmpp.bookmarks.add(room, data.name, nickname, autojoin);
      } else if (data.bookmarked) {
         // bookmarked === false
         jsxc.xmpp.bookmarks.delete(room, true);
      }

      jsxc.gui.dialog.close();

      return false;
   });
};