Page 1 of 1

Enhanced Bookmark Functionality

Posted: Sat Jan 14, 2012 6:06 pm
by boushley
I really like the features of the Study Notebook, however the bookmarks don't function quite like I would like them to. As far as I can tell there is no way to move bookmarks between articles / chapters. I'm not sure if this is by design, hasn't been implemented yet or if I just couldn't find the functionality. The situation I'm trying to fulfill is that I want a "Daily Book of Mormon" bookmark and a "Sunday School" bookmark that allow me to move them to where I'm at.

I've implemented something on my own that gives the basic functionality that I'm looking for. The js script here when loaded on a page with the study toolbar extends the bookmarks list to provide this functionality. Now when you click on "Bookmarks" in the study toolbar you can drag one of the items in the list onto the current page. The code will then update the bookmark so that it is on this new page.

Currently I'm using this in Google Chrome using this extension to put the script onto all pages that are part of If there are bugs people would like fixed or people are interested, I can turn this into a Chrome extension. Ideally it would be nice to see this get picked up and included as part of the study notebook itself.

Posted: Sat Jan 14, 2012 8:03 pm
by jdlessley
We can discuss this in the forums, but to get it before the development team is by using the Submit Feedback link on

Posted: Sat Jan 14, 2012 8:51 pm
by boushley
Thanks for pointing that out. I submitted it as feedback, but wanted to get it out here just in case anyone else was looking for similar functionality.

Posted: Tue Mar 06, 2012 8:24 am
by ndaman
This is an amazing idea while we wait for the development team to add the feature, I'm not so good at javascript, though, and I'm having trouble getting it to work. All I've done is paste your code into the javascript section of Personalized Web and made a little regex for the lds website, and I get this error on the javascript console: [PersonalizedWeb] Exception in custom JS code.
ReferenceError: jQuery is not defined
I tried adding some html to load jQuery but that didn't seem to work either. Thanks for this awesome script, I can't wait to get it working!

Posted: Tue Mar 06, 2012 9:45 am
by boushley
At the end of this post I've attached the PersonalizedWeb rule dump. You can load that dump and it should work for you. You have to embed it as 'Add HTML' so that it is in the same domain as the pages javascript. Try loading this rule, and let me know how that goes.

{"name":"LDS Bookmarks","urlRegex":"^https://(www\\.)?lds\\.org","urlExcludeRegex":"","enabled":true,"preserveDocWrite":false,"css":"","html":"<script type=\"text/javascript\">\n(function ($, undefined) {\n \u0020 \u0020if ((location.hostname !== '' && location.hostname !== '' ) || location.protocol !== 'https:')\n \u0020 \u0020 \u0020 \u0020return;\n\n \u0020 \u0020window.ExtendedLDSBookmarks = { };\n\n \u0020 \u0020var model = ExtendedLDSBookmarks.model = {\n \u0020 \u0020 \u0020 \u0020DRAG_THRESHOLD: 30,\n\n \u0020 \u0020 \u0020 \u0020bookmarks: null,\n \u0020 \u0020 \u0020 \u0020studyToolbar: null,\n\n \u0020 \u0020 \u0020 \u0020dragDeltaMet: false,\n \u0020 \u0020 \u0020 \u0020draggingStart: {x: null, y: null},\n \u0020 \u0020 \u0020 \u0020draggingBookmark: null,\n \u0020 \u0020 \u0020 \u0020startDraggingBookmark: function (id) {\n \u0020 \u0020 \u0020 \u0020 \u0020 \u0020for (var i = 0; i < this.bookmarks.length; i++) {\n \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020if (this.bookmarks[i].id === id)\n \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020{\n \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020this.draggingBookmark = this.bookmarks[i];\n \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020break;\n \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020}\n \u0020 \u0020 \u0020 \u0020 \u0020 \u0020}\n \u0020 \u0020 \u0020 \u0020},\n \u0020 \u0020 \u0020 \u0020stopDraggingBookmark: function () {\n \u0020 \u0020 \u0020 \u0020 \u0020 \u0020this.draggingBookmark = null;\n \u0020 \u0020 \u0020 \u0020},\n\n \u0020 \u0020 \u0020 \u0020addObserver: function (callback) {\n \u0020 \u0020 \u0020 \u0020 \u0020 \u0020this.callbacks = this.callbacks || [];\n \u0020 \u0020 \u0020 \u0020 \u0020 \u0020this.callbacks.push(callback);\n \u0020 \u0020 \u0020 \u0020},\n\n \u0020 \u0020 \u0020 \u0020notifyObservers: function (parameters) {\n \u0020 \u0020 \u0020 \u0020 \u0020 \u0020if (this.callbacks) {\n \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020var l = this.callbacks.length;\n \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020for (var i=0; i < l; i++) this.callbacks[i](parameters);\n \u0020 \u0020 \u0020 \u0020 \u0020 \u0020}\n \u0020 \u0020 \u0020 \u0020},\n\n \u0020 \u0020 \u0020 \u0020getDefaultTitle: function () {\n \u0020 \u0020 \u0020 \u0020 \u0020 \u0020return defaultTitle = $.trim($('title').html().replace(' ',' '));\n \u0020 \u0020 \u0020 \u0020},\n\n \u0020 \u0020 \u0020 \u0020getPageBaseUri: function () {\n \u0020 \u0020 \u0020 \u0020 \u0020 \u0020return document.location.pathname.replace(/\\..*/,'');\n \u0020 \u0020 \u0020 \u0020}\n \u0020 \u0020};\n\n \u0020 \u0020var commands = ExtendedLDSBookmarks.commands = {\n \u0020 \u0020 \u0020 \u0020initialize: function () {\n \u0020 \u0020 \u0020 \u0020 \u0020 \u0020console.log('Initializing Extended LDS Bookmarks');\n \u0020 \u0020 \u0020 \u0020 \u0020 \u0020this.loadToolbarInformation();\n \u0020 \u0020 \u0020 \u0020 \u0020 \u0020views.initialize();\n \u0020 \u0020 \u0020 \u0020},\n\n \u0020 \u0020 \u0020 \u0020loadToolbarInformation: function () {\n \u0020 \u0020 \u0020 \u0020 \u0020 \u0020model.studyToolbar = $('body').data().studyToolbar;\n\n \u0020 \u0020 \u0020 \u0020 \u0020 \u0020if (model.studyToolbar && model.studyToolbar.options)\n \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020model.bookmarks = model.studyToolbar.options.bookmarks;\n\n \u0020 \u0020 \u0020 \u0020 \u0020 \u0020if (!model.studyToolbar || !model.Bookmarks)\n \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020setTimeout(commands.loadToolbarInformation, 100);\n \u0020 \u0020 \u0020 \u0020},\n\n \u0020 \u0020 \u0020 \u0020startDraggingBookmark: function (id) {\n \u0020 \u0020 \u0020 \u0020 \u0020 \u0020model.startDraggingBookmark(id);\n \u0020 \u0020 \u0020 \u0020},\n\n \u0020 \u0020 \u0020 \u0020processBookmarkPlacement: function (placementPosition) {\n \u0020 \u0020 \u0020 \u0020 \u0020 \u0020var paragraph = $(model.studyToolbar.findParagraph(placementPosition, 'p[uri^=\"' + model.getPageBaseUri() + '\"]'));\n \u0020 \u0020 \u0020 \u0020 \u0020 \u0020var bookmark = model.draggingBookmark;\n \u0020 \u0020 \u0020 \u0020 \u0020 \u0020var uri = paragraph.attr('uri');\n \u0020 \u0020 \u0020 \u0020 \u0020 \u0020var pageUri = document.location.pathname;\n\n \u0020 \u0020 \u0020 \u0020 \u0020 \u0020// TODO Handle title updating\n \u0020 \u0020 \u0020 \u0020 \u0020 \u0020// However, for bookmarks being moved between areas I expect they'll have a more meaningful\n \u0020 \u0020 \u0020 \u0020 \u0020 \u0020// title. Like Sunday School Reading or Daily Scripture Study\n\n \u0020 \u0020 \u0020 \u0020 \u0020 \{\n \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020id:,\n \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020data: {\n \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020title: bookmark.title,\n \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020note: bookmark.note,\n \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020folders: bookmark.folderGuids,\n \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020tags: bookmark.tags,\n \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020source: (bookmark.source !== undefined) ? bookmark.source : '',\n \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020device: (bookmark.device !== undefined) ? bookmark.device : '',\n \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020pageUri : pageUri,\n \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020uri : uri\n \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020},\n \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020callback: function(data) {\n \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020if (model.studyToolbar.handleAjaxResponse(data)) {\n \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020model.studyToolbar.bookmark(data.response.annotation);\n \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020model.studyToolbar.resetBookmarkForm(true);\n \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020}\n \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020}\n \u0020 \u0020 \u0020 \u0020 \u0020 \u0020});\n\n \u0020 \u0020 \u0020 \u0020 \u0020 \u0020model.stopDraggingBookmark();\n \u0020 \u0020 \u0020 \u0020}\n \u0020 \u0020};\n\n \u0020 \u0020var views = ExtendedLDSBookmarks.views = {\n \u0020 \u0020 \u0020 \u0020initialize: function () {\n \u0020 \u0020 \u0020 \u0020 \u0020 \u0020var toolbar = $('#saveAndShareToolbar');\n \u0020 \u0020 \u0020 \u0020 \u0020 \u0020$('#bookmark-box .bookmark-list ul li', toolbar).live('mousedown', views.startDragging);\n \u0020 \u0020 \u0020 \u0020 \u0020 \u0020views.topOffset = $('#content').offset().top;\n \u0020 \u0020 \u0020 \u0020},\n\n \u0020 \u0020 \u0020 \u0020startDragging: function (e) {\n \u0020 \u0020 \u0020 \u0020 \u0020 \u0020commands.startDraggingBookmark($(this).attr('id'));\n\n \u0020 \u0020 \u0020 \u0020 \u0020 \u0020model.dragDeltaMet = false;\n \u0020 \u0020 \u0020 \u0020 \u0020 \u0020model.draggingStart.x = e.pageX;\n \u0020 \u0020 \u0020 \u0020 \u0020 \u0020model.draggingStart.y = e.pageY;\n\n \u0020 \u0020 \u0020 \u0020 \u0020 \u0020$(document).bind('mousemove', views.mouseMove);\n \u0020 \u0020 \u0020 \u0020 \u0020 \u0020$(document).bind('mouseup', views.stopDragging);\n\n \u0020 \u0020 \u0020 \u0020 \u0020 \u0020e.stopImmediatePropagation();\n \u0020 \u0020 \u0020 \u0020 \u0020 \u0020return false;\n \u0020 \u0020 \u0020 \u0020},\n\n \u0020 \u0020 \u0020 \u0020mouseMove: function (e) {\n\n \u0020 \u0020 \u0020 \u0020 \u0020 \u0020if (!model.dragDeltaMet) {\n \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020var currentPoint = {x: e.pageX, y: e.pageY};\n \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020if (util.distance(model.draggingStart, currentPoint) > model.DRAG_THRESHOLD)\n \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020{\n \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020model.dragDeltaMet = true;\n\n \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020views.mark = $('<div></div>').addClass('bookmark').css({\n \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020'top': e.pageY - views.topOffset\n \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020}).appendTo('#primary');\n \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020}\n\n \u0020 \u0020 \u0020 \u0020 \u0020 \u0020}\n\n \u0020 \u0020 \u0020 \u0020 \u0020 \u0020if (model.dragDeltaMet)\n \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020views.mark.css({'top': e.pageY - views.topOffset});\n \u0020 \u0020 \u0020 \u0020},\n\n \u0020 \u0020 \u0020 \u0020stopDragging: function (e) {\n \u0020 \u0020 \u0020 \u0020 \u0020 \u0020if (model.dragDeltaMet)\n \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020 \u0020commands.processBookmarkPlacement(e.pageY);\n\n \u0020 \u0020 \u0020 \u0020 \u0020 \u0020$(document).unbind('mousemove', views.mouseMove);\n \u0020 \u0020 \u0020 \u0020 \u0020 \u0020$(document).unbind('mouseup', views.stopDragging);\n \u0020 \u0020 \u0020 \u0020 \u0020 \u0020views.mark = null;\n\n \u0020 \u0020 \u0020 \u0020 \u0020 \u0020e.stopImmediatePropagation();\n \u0020 \u0020 \u0020 \u0020 \u0020 \u0020return false;\n \u0020 \u0020 \u0020 \u0020},\n \u0020 \u0020};\n\n \u0020 \u0020commands.initialize();\n\n \u0020 \u0020// Utility function\n \u0020 \u0020ExtendedLDSBookmarks.util = util = {\n \u0020 \u0020 \u0020 \u0020distance: function (point1, point2) {\n \u0020 \u0020 \u0020 \u0020 \u0020 \u0020var xSquare = point2.x - point1.x;\n \u0020 \u0020 \u0020 \u0020 \u0020 \u0020xSquare *= xSquare;\n\n \u0020 \u0020 \u0020 \u0020 \u0020 \u0020var ySquare = point2.y - point1.y;\n \u0020 \u0020 \u0020 \u0020 \u0020 \u0020ySquare *= ySquare;\n\n \u0020 \u0020 \u0020 \u0020 \u0020 \u0020return Math.sqrt(xSquare + ySquare);\n \u0020 \u0020 \u0020 \u0020}\n \u0020 \u0020};\n})(jQuery)\n</script>","js":"","filters":[]}

Posted: Tue Mar 06, 2012 11:55 am
by ndaman
Works like a champ, thanks again for the wonderful code!

Posted: Tue Mar 06, 2012 11:57 am
by boushley
Glad to hear it! Hopefully it will get integrated into the site.

Posted: Thu Mar 15, 2012 2:47 pm
by barkeraj
We know that the bookmark functionality is a bit wonky (technical term). Bookmark has different meanings to different people. There are bookmarks that the browser has, which is always the same, and bookmarks used in a book which you update regularly.

We do have a bit of an overhaul to the study tools coming in an unspecified point in the future (hint, before the millenium) where we will make sure to address both desires.

Posted: Thu Mar 15, 2012 2:55 pm
by boushley
Glad to hear there is an overhaul. Hopefully this functionality will be built in. The ability to just pull a bookmark from the bookmark list and place it on the screen is awesome. I assumed that highlights filled the bookmarks like browser bookmarks area. Especially since you could already move bookmarks within the given page, so I just reused that api to move bookmarks between pages.

I look forward to the new work.

Re: Enhanced Bookmark Functionality

Posted: Mon Mar 28, 2016 5:29 am
by rrichey4430
This used to work for me (after a few tweaks) but no longer works. When I get some time I will troubleshoot I will try to figure out what the problem is. Anyone else using this successfully in 2016?