Skip to content

Kitaboo SDK components

TOC (Table of Contents)

Introduction

The Table of Contents (TOC) contains all the information about book pages and contents. This feature can be used to generate a Table of Contents for the project. The TOC can be used to navigate to a specific text, pages or information in a book through Index terms and Bookmarks.

Implementation

Kitaboo SDK supports a default TOC view to show the different view as per tabs. User can customise TOC view in their own ways.

Basic view for TOC

Use the following code to add TOC to your reader :

Basic integration for TOC tab
// toc view implementation started
            var container = document.getElementById('tocholder');
            var defaultOpts = {
                container: container,
                data: render.book.bookdata.toc
            }
            /**
             * option for toc
             * container: container which holds the toc view
             * data: toc data
             */
            toc = new TOCManager(defaultOpts);

Basic integration for Bookmark tab
if (bookmarkobj.bookmarkManager.listofbookmarks.length == 0) {
            $("#bookmarkholder").html("<span class='not-available'>No Bookmarks Available</span>");
        } else {
            var bookmarkContainer = document.getElementById('bookmarkchild');
            var bookmarkData = [];
            for (var i = 0; i < globaledata.length; i++) {
                if (globaledata[i].type == 4 && globaledata[i].status != 'DELETE') {
                    bookmarkData.push(globaledata[i]);
                }
            }
            bookmarkobj.createBookmarkView(bookmarkData, bookmarkContainer);
        }

Basic integration for Resource tab
/**
                 * option for resource
                 * container: container which holds the resource view
                 */
                // resources with their id on server. Map the server id of resource to it's type 
                var resourcesConvertor = {
                    'documents'        : 1,
                    'video'            : 2,
                    'audio'            : 3,
                    'image'            : 4,
                    'web links'        : 5,
                    'htmlwrap'         : 6,
                    'activity'         : 7, 
                    'toc'              : 8,
                    'pagestatus'       : 9,
                    'kaltura_video'    : 10, 
                    'external_web_link': 11,
                    'textinput'        : 12,
                    'dropdown'         : 13,
                    'external_image'   : 14,
                    'external_audio'   : 15,
                    'external_video'   : 16,
                    'external_documents': 17,
                    'kitaboowidget'    : 18,
                    'external_htmlwrap': 19,
                    'youtube_video'    : 21,
                    'survey'           : 22,
                    'slideshow'        : 23,
                    'glossary'         : 24,
                    'imagezoom'        : 25,
                    'jumptobook'       : 26,
                    'external_imagezoom': 27,
                    'standalone_instruction' : 28,
                    'teacher_exclusive': 29,
                    '1'                : 1,
                    '2'                : 2,
                    '3'                : 3,
                    '4'                : 4,
                    '5'                : 5,
                    '6'                : 6,
                    '7'                : 7,
                    '8'                : 8,
                    '9'                : 9
                }
                // resource config
                var resourceParams = { container: resourceContainer };
                // create resource object
                resourceManager = new ResourceManager(resourceParams);

                // create filter resources collections which we can show in tab
                var resourcesCollection = [];
                book.resources.forEach(function(returnableObjects){
                    var resource = returnableObjects.subitems.filter(function (item) {
                        // hide toc, page status, weblink, gotopage, glossary, jumptobook, fib, and dropdown resources
                        return ([5, 8, 9, 12, 13, 24, 26].indexOf(Number(resourcesConvertor[item.typeId.toLowerCase()])) < 0 || item.displayUnderTOR);
                    });
                    resourcesCollection.push({id: returnableObjects.id, title: returnableObjects.title, subitems: resource});
                });

                //assign collection to create  view for resources in Resource tab
                resourceManager.createResourceView(resourcesCollection);

Customization

Kitaboo SDK supports a TOC component to show the table of contents of a book in the reader. You can either customize the entire TOC component or make simple changes to the TOC that comes with Kitaboo reader by just changing the font and CSS. If you want to replace the entire TOCView with your own, then you can just use the data and create your own view.

1. Basic customization

Using CSS you can customize the following:

1.1. Theme

Allows you to set the theme color using id #tocchild and its child components

1.2. Add or Remove tabs

This will allow you to remove tabs in TOC view or change label etc.

var toc-tab-btn= document.getElementsByClassName('bookmark-tab-btn');

var bookmarkTab = document.getElementsByClassName('bookmark-tab-btn');

var resourceTab = document.getElementsByClassName('resources-tab-btn');
                resourceTab[0].style.display = "none";

2. Advanced customization

Creating your own custom Toc View and utilizing the same on Reader.

Here, we are going to display TOC data in a custom list (using repeat). On click of TOC menu item, define your custom listview and render same using data in tocData object.

tocdata = book.bookdata.toc

On click of Bookmark tab menu item, define your custom listview and render the same using data in bookmarkData, and we need to decode metadata of bookmark object so that it becomes readable content and can display it in view.

bookmarkData = [];
    $scope.bookmarkData = globaledata, {
        'type': 4,
        'status': '!DELETE'
                            };
    if (bookmarkData.length > 0) {
        for(var count = 0; count <bookmarkData.length ;count ++){
            if (bookmarkData[count ].metadata !== "" && bookmarkData[count ].metadata.createdOn === undefined) {
                bookmarkData[count ].metadata = JSON.parse(decodeURIComponent(bookmarkData[count ].metadata));
                    }
                        });
                            }

On click of Resource tab menu item, define your custom listview and render the same using data in resourcesData, and we can use the data to display the icons and labels and resourcesLength can be used to display the no data message if no resources are available.

/**
* option for resource
* container: container which holds the resource view
*/
                // resources with their id on server. Map the server id of resource to it’s type 
                var resourcesConvertor = {
                    'documents': 1,
                    'video': 2,
                    'audio': 3,
                    'image': 4,
                    'web links': 5,
                    'htmlwrap': 6,
                    'activity': 7, //Porto is 6
                    'toc': 8,
                    'pagestatus': 9,
                    'kaltura_video': 10, // Porto is 16
                    'external_web_link': 11,
       'textinput': 12,
                    'dropdown': 13,
                    'external_image': 14,
                    'external_audio': 15,
                    'external_video': 16,
                    'external_documents': 17,
                    'kitaboowidget': 18,
                    'external_htmlwrap': 19,
                    'youtube_video': 21,
                    'survey': 22,
                    'slideshow': 23,
                    'glossary': 24,
                    'imagezoom': 25,
                    'jumptobook': 26,
                    'external_imagezoom': 27,
                    'standalone_instruction': 28,
                    'teacher_exclusive': 29,
                    '1': 1,
                    '2': 2,
                    '3': 3,
                    '4': 4,
                    '5': 5,
                    '6': 6,
                    '7': 7,
                    '8': 8,
                    '9': 9
                };
                // resource config
                var resourceParams = {
                    container: resourceContainer,
                };
                // resourceManager = 
                new ResourceManager(resourceParams);
                // create filter resources collections which we can show in tab
                var resourcesCollection = [];
                book.resources.forEach(function (returnableObjects) {
                    var resource = returnableObjects.subitems.filter(function (item) {
 // hide toc, page status, weblink, gotopage, glossary, jumptobook, fib, and dropdown resources
                        console.log([5, 8, 9, 12, 13, 24, 26].indexOf(Number(resourcesConvertor[item.typeId.toLowerCase()])), '[5, 8, 9, 12, 13, 24, 26].indexOf(Number(resourcesConvertor[item.typeId.toLowerCase()]))', item);
                        return ([5, 8, 9, 12, 13, 24, 26].indexOf(Number(resourcesConvertor[item.typeId.toLowerCase()])) < 0 && item.displayUnderTOR);
                    });
                    resourcesCollection.push({
                        id: returnableObjects.id,
                        title: returnableObjects.title,
                        subitems: resource
                    });
                });
                if (resourcesCollection.length > 0) {
                    resourcesData = resourcesCollection;
                   resourcesLength = true;
                } else {
                   resourcesLength = false;
                }
            }

Now, you can perform required action on Toc item click.

  • Toc Item click event callback

As we are using sdk toc data so we can pass the complete tocObject to navigateToPage method and it will redirect to desired page.

render.navigateToPage(tocObject);

And any item navigation for bookmark and resource just need to pass folioID

And we are displaying bookmark and resources in custom view so the object is not same as SDK so we can pass the folioID and startWordIndex and EndWordIndex to navigateToPage method and it will redirect to the desired page.

  • Bookmark and resource navigation
render.navigateToPage({
                        "href": '#bookmark_' + (obj.folioID) + ':' + obj.metadata.StartWordIndex + "_" + obj.metadata.EndWordIndex,
                        "spinePos": parseInt(obj.folioID) 
                    });

Highlight

Introduction

Use the Highlight tool to select text to highlight it with different colors or add a contextual note. You can also highlight data based on an already-performed activity.

Implementation

Kitaboo SDK provides the flexibility to customize the highlight color, which means the user can add multiple highlight color icons,contextual note icons, search and delete icons on the highlight panel.

To customize the highlight tool, you need to first create and initialize the CSS for the toolbar using the following code :

1. Initialization

Highlight component with config and pass desired style in config defaultHighlighCSS object

Note

We can only customize the UI of delete highlight icon as it is coming from sdk. It only appears if you click on already highlighlighted area. We get that object in HIGHLIGHT_CLICKED event only

/**
  * Highlight and Note contextual menu configuration object
  * @param {object} config
  * @param {string} [config.textSelectionCSS] :
  */
var config = {
       textSelectionCSS: "color:white; background:red",
       defaultHighlighCSS:"border-radius:23px;opacity:1.0;padding-left:5px;padding-right: 5px;background-color: rgba(0, 0, 0, 0.6) !important;display:none;height:50px;",
                stickynotetext: "¸", // icon for sticky note
                contextualnoteicon: 'V', // icon for context note
                isKitabooHighlightsupport: (book.bookdata.metadata.layout === "pre-paginated")// kitaboo native books highlight support
}
// create highlight class object
var highlight = new kitaboohighlight(config);
highlight.setScrollMode(true); //if reflowable default have scroll mode on

Once you have created your custom toolbar, follow the below events to capture user actions and update the view

2. Create Highlight Button

To add the Highlight colors, either you can add object of highlight one by one or you can add them dynamically using iterative methods

for example: HighlightColor is an array of colors for Highlight.

for (var count= 0; count < HighlightColor.length ; count++){ 
                var highlightId = 'highlight+_'+ count;         // any id can be provided   
                var item = {
                    icon: '"',
                    id: highlightId ,
                    css: "color:" + HighlightColor[count ]
                };
                highlight.addItem(item);
            });
  • Add button in Highlight Panel
highlight.addItem(item);

Highlight Listeners

Highlight Colors are already added in context menu. Now the item click event handles its functionality and we can also add any additional functionality to customize the behaviour.

highlight.on(highlight.HIGHLIGHT_MENU_ITEM_CLICKED, function(highlightobject) {
if (highlightobject.item.id == ' 'highlight+_'+ count;') { // check all the id's used for highlight and handle here
                    var drawcss = {
                        backgroundColor: highlightobject.item.style.color,
                        color: "",
                        element: "span"
                    }
                    highlightobject.highlight.important = "N";
                    if (highlightobject.highlight.status == 'update') {
                        highlight.editHighlight(highlightobject.highlight, drawcss);
                        for (var i = 0; i < globaledata.length; i++) {
                            if (globaledata[i].id == highlightobject.highlight.id) { /*update highlight object in globaledata*/
                                globaledata[i].status = 'update';
                                globaledata[i].important = "N";
                                break;
                            }
                        }
                    } else if (highlightobject.highlight.id == 0 && MODE == 'EDIT') {
                        var jsondata = JSON.parse(decodeURIComponent(highlightobject.highlight.metadata));
                        for (var i = 0; i < globaledata.length; i++) {
                            var object = JSON.parse(decodeURIComponent(globaledata[i].metadata));

                            if (jsondata.StartWordIndex == object.StartWordIndex && jsondata.EndWordIndex == object.EndWordIndex) {
                                globaledata.splice(i, 1);
                                highlight.addHighlight(document.activeElement.contentDocument, highlightobject.highlight, drawcss);
                            }
                        }
                        highlight.hide();
                    } else {
                        highlight.addHighlight(document.activeElement.contentDocument, highlightobject.highlight, drawcss);
                    }

                } else {
                        highlight.addHighlight(document.activeElement.contentDocument, highlightobject.highlight, drawcss);
                    }
                    //globaledata.push(highlightobject.highlight);
                } else if (highlightobject.item.id == '102') {
                    highlight.hide();
                    highlightobject.highlight.type = 2;
                    highlightnote.showInEditMode(highlightobject.highlight);
                }
                highlightEnable = false; // highlight button state
                $('.highlightBttn').toggleClass('active', highlightEnable);// toggle highlight button class
                render.enableHighlight(highlightEnable); // enable highlight button
                render.showAllMarkups(); // show all markups
}); 
// add the create object in highlight object. when user selects text on document. 
// Popup will contain the above custom object(item1, item2)
Examples:
highlight.addItem(item1);
highlight.addItem(item2);
highlight.addItem(item3);


        // add sticky note in DOM
        render.on("sticky:add", function(object) {
            console.log(object)
            highlightnote.showInEditMode(object);
        });
        // draw custom highlight


        highlight.on(highlight.HIGHLIGHT_CLICKED, function(assetobject) {
            highlight.showInEditMode(assetobject);
           $("#800").css({ // Css for Highlight delete icon
                    "color": "#7aa2bc",
                    "font-size": "22px"
                });

        });
        // remove the highlight or note for custom items

You can call Save on user interaction

        highlight.on(highlight.HIGHLIGHT_REMOVE, function(highlightobject) {
                });

        // update the drawn higlight or note or and custom item with new values
        highlight.on(highlight.HIGHLIGHT_UPDATE, function(highlightobject) {
            console.log('update', highlightobject);
        });
        // event dispatched add highlight object to collection
        highlight.on(highlight.HIGHLIGHT_ADD, function(highlightobject) {
        });
 /**
* event dispatched on custom item clicked in context menu
    * custom item create by user and information.
    * using which he/she can manipulate the selection on DOM 
    */

Customization

  • Set background of highlight selection, set selection area shape, set items text color, set stroke width in defaultHighlighCSS and passing it in config while intialization defaultHighlighCSS: "border-radius: 23px;opacity: 1.0;padding-left: 5px;padding-right: 5px;background-color: rgba(0, 0, 0, 0.6) !important;display:none;height:50px;",

  • We can customize the delete highlight icon as well, but as it is predefined and only appears if you click on already highlighlighted area, we get that object in HIGHLIGHT_CLICKED event only.

$("#800").css({ // Css for Highlight delete icon
"color": "#7aa2bc",**
"font-size": "22px"**
 });**

Draw Highlight on the Document

To get the selected text of Highlighted Feature

// once DOM selection complete. show popup for editing
render.on("highlight:section", function(textobject, page) {
 highlightrange = textobject;
 highlight.show(textobject, page);
});

Introduction

You can search for specific data from the book content.

Implementation

Use the following code to search for text in a book:

/**
  * @property: (book.bookdata.metadata.layout  === "pre-paginated")
  * checked if book is fixed layout book, search.json logic will work
   * else reflow loading all DOM pages and creating page collection for search
   * for DOM implementation call function as below
   * searchObj.parseSearchFromPackage(bookObj)
   * above function requires book object
   */
 var config = {
                kitabooSeachSupport: true, // default check search.json
                groupSearchResultForPage: (book.bookdata.metadata.layout  !== "pre-paginated") // group search result in reflow book
}            
search = new KitabooSearch(renderer.book, config);
search.on("search:fail",function(response){
                console.log(response, 'search file failed to load');

                var search_container = document.getElementsByClassName('search-container');
                if(search_container != null){
                   // search_container[0].style.display = 'none';
                }
            });
            search.loadSearchData();
            search.on("search:result", function(searchresult) {
                if (searchresult.length == 0) {
                    render.clearAndHighlightSearch();
                }
                search.setData(searchresult);
                var $filterdata = searchresult;
                if ($filterdata.length === 0) {
                    $('.search-panel').css('height', '90px');
                } else {
                    var dynamicHeight = ($filterdata.length * 100) > ($(window).height() - 170) ? ($(window).height() - 170) : ($filterdata.length * 100);
                    $('.search-panel').css('height', (dynamicHeight + 60 /*padding*/ ) + 'px');
                }

                search.show();
            });

            search.on("search:ItemClick", function(searchOject) {
                render.search(searchOject);
                search.hide();
            });

Customization

Kitaboo SDK supports a Search component to show the result of the search made for the content of the book. You can either customize the entire Search component or make simple changes to the Search that comes with Kitaboo reader by just changing the font and CSS. If you want to replace the entire Search with your own, then you can just use the data and create your own view.

1. Basic customization

Using CSS you can customize the following:

Theme

Allows you to set the theme color using id #searchlist and its child components.

2. Advanced customization

Creating your own custom Search View and utilizing the same on Reader.

Here, we are going to display Search data in a custom list (using repeat).

  • On click of Search button, define your custom listview and render same using data in searchresult object.
search.on("search:result", function(searchresult) {
                if (searchresult.length == 0) {
                    render.clearAndHighlightSearch();
                }
  • On click of Search Item, define your custom action and use the highlighting of searched item of reader together using below mentioned line.
render.search(searchOject);

Example Project

The Search Example project is a sample code reference of SDK Integration with fully functioning Search Component. Click here to Download the project.

Note

Introduction

Note is a piece of information that users can put on the page. Kitaboo Sdk facilitates Note feature, which can be used for saving page data or user comments on page. The Note feature allows readers to create and share notes. You can associate a note to a word, phrase, sentence, paragraph or multiple paragraphs.

Contextual note: Note creation is done using selected text where position of note depends upon selected text position.

Sticky note: Note creation is done by clicking anywhere in Page-area, where position of note depends on selected page area (X,Y) points.

Implementation

Contextual Notes and Sticky Notes are the same here, we are only differentiating using highlighted text. If selected text is shown in note then it's contextual note otherwise sticky note.

Use the following code to add notes to your reader :

        /*
             * create a note configuration object
             * @param {object} noteconfig: {locale: {buttons: {}, messages: {}}, container: document.body}
             */
            var noteconfig = {
                locale: {
                    buttons: {
                        cancel: 'Cancel', post: 'Post', share: 'Share', done: 'Done', close: 'Close'
                    },
                    messages: {
                        shareInNotePopup: 'Share this note with',
                        notePlaceHolder: 'Enter your note',
                        highlightImportant: 'Click to make this note important',
                        highlightNormal: 'Click to make this note normal'
                    }
                },
                isDefaultNotesPopup: true
            }
        /*
         * create a note object
         * @param {object} noteconfig: {textSelectionCSS: '', defaultHighlighCSS: ''}
         */
        var highlightnote = new KitabooNotes(noteconfig);
noteHighlightData = globaledata.filter(function(data){
                return ((data.type === 1 || data.type === 2) && data.status != 'DELETE');
            });
            highlightnote.setUserVO(syncManager.syncAdapter.userVO); // assign current user
            highlightnote.setNoteShareData(noteHighlightData); // note data from server
            highlightnote.setNoteData(shareSettingData); // shared setting
            highlightnote.setShareData(shareData); // shared data

Events :

// save highlight or notes data
        highlightnote.on(highlightnote.SAVE_NOTES, function(highlightobject) {
            var drawcss = {
                backgroundColor: highlightobject.important == true ? "red" : "yellow",
                color: "",
                element: "span"
            }
            highlight.addHighlight(document.activeElement.contentDocument, highlightobject, drawcss);
        });
/* sticky note updated */
highlightnote.on('sticky:update', function(noteObject) {
                for (var i = 0; i < globaledata.length; i++) {
                    if (globaledata[i].id == noteObject.id) {
                        globaledata[i] = noteObject;
                        console.log(globaledata[i]);
                        break;
                    }
                }
            });
        // contextual note click
        highlightnote.on("note:stickyClick", function(highlightobject) {
            highlightnote.showInEditMode(highlightobject);
        });
        // contextual note deleted
        highlightnote.on(highlightnote.NOTE_DELETED, function(id, highlightobject) {
        });
//share note data
highlightnote.on("note:onDoneClick", function(ugcData, noteShareDataObject) {
});

Customization

If you are using your custom popup, then you have to follow the below mentioned steps :

isDefaultNotesPopup : false // so default pop should not appear
Open your popup in below mentioned events
sticky:add
"note:stickyClick
3) On Text change call  highlightnote.onNotesTextChanges(event);
4) On Save call  highlightnote.onPostClick(event);
5) On Delete call highlightnote.onDeleteClick(event);

Bookmark

Introduction

Kitaboo Sdk exposes a component which helps the user to create a list of favorite pages. List of Bookmark is available in Toc View in the Bookmark Tab.

A bookmark is a saved shortcut that directs you to a specific page in the book. It stores the title, text, and favicon of the corresponding page. Saving bookmarks allows you to easily access your favorite locations in the Book.

Implementation

Use the following code to add a bookmark to your reader :

// bookmark started
        var option = {
            defaultPopup: true, // if default popup is needed
            charlimit: 100, // character limit for title for bookmark
            bookmarkpostion: "right", // bookmark placement on right or left
            bookmarkselectcolor: '#43dbf2', // selection color
            bookmarkunselectcolor: '#a9a9a9', // default color
            container: document.body, // parent
            book: book, // book
            bookmarkIcon: 'q', // bookmark icon font code
            locale: { // locale
                label: {
                    remainingChars: 'Chars: ', // label for max character
                },
                buttons: { // buttons label
                    add: 'Add', // add bookmark button label
                    save: 'Save', // update bookmark button label
                    delete: 'Delete', // delete bookmark button label
                },
                messages: {
                }
            }
        }
        // create bookmark with above options
        var bookmarkobj = new Bookmark(option);

Events :

        /**
         * event triggers when current active pages are visible
         * @return {object} event: contains chapter and document information
         * @return {object} event.chapter: current loading chapter object
         * @return {object} event.data: current loading document
         */
        renderer.on("viewer:doc_loaded", function(event){
          var chapter = event.chapter;
            bookmarkobj.setLabel(chapter.chapterName);
            bookmarkobj.addViewOnPage(chapter, event.data);
            var listofbookmark = getPagebookmarks(chapter);
            bookmarkobj.addBookmarkOnPageFromUGC(listofbookmark);
        });

        /**
         * event triggers when current active pages are visible
         * @return {number} current active page number
         */
        renderer.on("viewer:pagechange", function(event){
            bookmarkobj.checkBookmark(event);
                bookmarkobj.hide();
        });

Customization

  • We can customize the bookmark icon by changing the bookmarkIcon in option.

bookmarkIcon: 'q', // bookmark icon font code

  • We can change the locale to provide the custom messages.
 locale: { // locale
       label: {
 remainingChars: 'Chars: ', // label for max character
  },
 buttons: { // buttons label
          add: 'Add', // add bookmark button label
           save: 'Save', // update bookmark button label
            delete: 'Delete', // delete bookmark button label
                },
                messages: {
                }
  • We allow you to set the theme color using class ".bookmark-popup" and its child components

  • We can use custom bookmark popup replacing default popup

defaultPopup: false, // if default popup is needed

MyData

Introduction

MyData is a feature supported by Kitaboo Sdk which helps to display the total number of Notes (Contextual and Sticky Note) and Highlights available for a book in a listview under "MyData". Here you will get information like: chapter name, page id, saved page data, data & time. It consists of shared[Notes/highlight] data between multiple users as well.

Implementation

Use the following code in your reader :

// MYData view implementation started eg., highlight, notes
 var myDataContainer = document.getElementById("mydatalist");
            userdataObj = new MYData({
                container: myDataContainer
            });
            /**
             * MYData event handler
             * @param {string} [spinePos] : index in spineItem(refer book data)
             **/
            userdataObj.on("mydata:itemClick", function(mydataobject) {
                // hide the MYData container
                togglePopUpContainer('mydata-panel');
                render.navigateToPage(mydataobject);
                //highlightnote.shareNoteShowInEditMode(mydataobject);
            });

       userdataObj.on("data:shareReceive", function(shareDataObject) {
                //togglePopUpContainer('mydata-panel'); // needed if we want to close popup
                $('#myDataConfirmation').show();
                /*object passing to saveHighlightSetting service*/
                syncManager.syncAdapter.saveHighlightSetting(usertoken, bookId, shareDataObject, function(response) {
                    saveHighlight = response;
                });
            });
            userdataObj.on("mydata:postComment", function(commentObj) {
                     for (var i = 0; i < globaledata.length; i++) {
                    if (globaledata[i].id == commentObj.id) {
                        globaledata[i] = commentObj;
                        break;
                    }
                }
            });
            userdataObj.on("mydata:acceptClick", function(Noteacceptobj) {
                for (var i = 0; i < noteHighlightData.length; i++) {
                    if (noteHighlightData[i].id == Noteacceptobj.id) {
                        noteHighlightData[i].actionTaken = 'Y';
                    }
                }

          for (var i = 0; i < globaledata.length; i++) {
                    if (globaledata[i].id == Noteacceptobj.id) {
                        globaledata[i].actionTaken = 'Y';
                    }
                }
                var acceptUgcList = {
"acceptUgcList": [{
"ugcID": Noteacceptobj.id,
"status": 1
                    }]
                }
                syncManager.syncAdapter.acceptCollabData(usertoken, bookId, acceptUgcList, function(response) {
                    if (response.responseCode == 200) {
                        console.log(response, 'acceptCollabData');
                    }
                });
                if ((unacceptCount - 1) > 0) {
                    $('.shared-note-count')[0].innerHTML = unacceptCount - 1;
                    unacceptCount = unacceptCount - 1;
                } else {
                    $('.shared-note-count').css("display", "none");
                }
            });

       userdataObj.on("mydata:rejectClick", function(Noteacceptobj) {
                for (var i = 0; i < noteHighlightData.length; i++) {
                    if (noteHighlightData[i].id == Noteacceptobj.id) {
                        noteHighlightData.splice(i, 1);
                        console.log(noteHighlightData[i]);
                    }
                }
                for (var i = 0; i < globaledata.length; i++) {
                    if (globaledata[i].id == Noteacceptobj.id) {
                        globaledata.splice(i, 1);
                    }
                }
                var acceptUgcList = {
"acceptUgcList": [{
"ugcID": Noteacceptobj.id,
"status": 0
                    }]
                }
                syncManager.syncAdapter.acceptCollabData(usertoken, bookId, acceptUgcList, function(response) {
                    if (response.responseCode == 200) {
                        console.log(response);
                    }
                });

                if ((unacceptCount - 1) > 0) {
                    $('.shared-note-count')[0].innerHTML = unacceptCount - 1;
                    unacceptCount = unacceptCount - 1;
                } else {
                    $('.shared-note-count').css("display", "none");
                }
            });

            userdataObj.on("mydata:SaveNoteshare", function(ugcData) {
                togglePopUpContainer('mydata-panel');
                isDoneClick = true;
                var ugcBookList = { /*object contains bookId,book version,createdOn,pageNo,ugcData etc...*/
"ugcBookList": [{
"bookID": bookId.toString(),
"bookVersion": "2.0",
"ugcList": [ugcData[0]]
                    }]
                }
                var object = JSON.parse(decodeURIComponent(ugcBookList.ugcBookList[0].ugcList[0].metadata));
                /*ugcData contains bookId,book version,createdOn,pageNo,ugcData etc...*/
                if (isServiceAdapator) {
                    syncManager.syncAdapter.saveUGC(usertoken, ugcBookList, function(response) {
                        if (response.responseCode == 200) {
                            console.log(response);
                            var ugcId = response.bookUgcList[0].ugcList[0].id;
                            console.log(ugcId);
                            if (response.responseCode == 200) {
                                ugcData[1].collabData[0].ugcID = response.bookUgcList[0].ugcList[0].id; /*assign value of current note id to noteShareObject*/
                                /*noteShareData contains list of receivers and ugclist*/
                                syncManager.syncAdapter.saveCollabData(bookId, usertoken, ugcData[1], function(response) {
                                    console.log(response);
                                    syncManager.syncAdapter.fetchCollbShareData(usertoken, bookId, function(response) {
                                        if (response.responseCode == 200) {
                                            console.log(response);
                                        }
                                    });
                                });
                            } else {
                                console.log('Data saving Failed.!')
                            }
                        }
                    });
                }
                var drawcss = {
                    backgroundColor: object.color, //ugcData.ugcBookList[0].ugcList[0].important == "Y" ? "red" : "yellow",
                    color: "",
                    element: "span"
                }
            });
            // MYData view implementation end

Customization

Kitaboo SDK supports a MyData component to show the list of Notes and Highlights available for the book. You can either customize the entire MyData component or make simple changes to the MyData that comes with Kitaboo reader with just changing the font and CSS. If you want to replace the entire MyData with your own, then you can just use the data and create your own view.

1. Basic customization

Using CSS you can customize the following :

1.1. Theme

You can set the theme color using id #mydatalist and its child components

1.2. Changing labels

This will allow you to change label in MyData Panel

userdataObj = new MYData({
                container: myDataContainer,
                locale: {
                    label: {
                        notes: 'Notes', highlight: 'Highlights', myNotes: 'My Notes', 
                        myHighlights: 'My Highlights', chapter: 'Chapter:', page: 'Page'
                    },
                    buttons: {
                        shared: 'Shared with Me', comment: 'Comments', share: 'Share'
                    },
                    messages: {
                        shareMsg: ' has started sharing data with you.',
                        nodata: 'No data found.'
                    }
                }
            });

Example Project

The My-data Example project is a sample code reference of SDK Integration with fully functioning My-data Component. Click here to Download the project.

Pen

Introduction

KitabooSdk supports a feature called "pen marker" which can be utilized to draw/pen-mark, It supports multiple types of lines/shapes of different colors on book pages with various thickness.

Implementation

Use the following code to add Pen to your reader :

Note

To display pen cursor dynamically, we use the cursor_png and assign it to cursor, but it needs to be written differently for IE and edge

// resolved IE issue for dynamic cursor
        var penCursor = "url('" + <basePath> + "css/images/cursor_pen.png') 5 23, auto";
        if (BrowserDetect.browser === 'Internet Explorer' || window.navigator.userAgent.indexOf("Edge") > -1) {
            penCursor = "url('" + <basePath> + "css/images/cursor_pen.cur'), auto";
        } 
/* drawing canvas implementation*/
        var drawingOptions = {
            mode: 'pen', // set mode
            lineWidth: '3', // starting line width
            fillStyle: '#FFFFFF', // starting fill style
            strokeStyle: $('.pick-colors.color-active').css("color"), // start stroke style
            cursors: penCursor // set to default
        };

        // drawing object for single page
        drawCanvas = new DrawingCanvas(drawingOptions);
        /**
         * if mouse down on single page
         * @return {object} event
         * @return {object} event.canvas: current mouse up canvas
         * @return {object} event.data: current mouse up position
         * @return {object} event.target: parent or this
         **/
        drawCanvas.on('mouse:down', function(event){
             console.log(event, 'drawCanvas mouse:down');
//drawCanvas.tools: tools used for drawing pen and eraser
// drawCanvas.defaults.mode: drawing options when creating drawingCanvas Object 
            drawCanvas.tools[drawCanvas.defaults.mode](event.target, event.data);
        });
        /**
         * if mouse move on single page
         * @return {object} event
         * @return {object} event.canvas: current mouse up canvas
         * @return {object} event.data: current mouse up position
         * @return {object} event.target: parent or this
         **/
        drawCanvas.on('mouse:move', function(event){
            //console.log(event, 'drawCanvas mouse:move');
            drawCanvas.tools[drawCanvas.defaults.mode](event.target, event.data);
        });
    /**
         * if mouse up on single page
         * @return {object} event
         * @return {object} event.canvas: current mouse up canvas
         * @return {object} event.data: current mouse up position
         * @return {object} event.target: parent or this
         **/
        drawCanvas.on('mouse:up', function(event){
console.log(event, 'drawCanvas mouse:up');
            var currentObj = drawCanvas.tools[drawCanvas.defaults.mode](event.target, event.data);;
            ugcBookList = {
"ugcBookList": [{
"bookID": bookId,
"bookVersion": "2.0",
"ugcList": [currentObj]
                }]
            }
            if (isServiceAdapator && Object.keys(currentObj).length) {
                /*student can not delete submitted pen data and teacher's annotation*/
                var isStudentDataSubmitted = (currentObj.hasOwnProperty('submitted') && currentObj.submitted);
                if (!isStudentDataSubmitted) {
                    syncManager.syncAdapter.saveUGC(usertoken, ugcBookList, function(response) {
                        if (response.responseCode == 200) {
                            if (drawCanvas.defaults.mode === 'eraser') {
                                for (var i = globaledata.length - 1; i >= 0; i--) 
                                {
                                    if (currentObj.pageId === globaledata[i].pageId && currentObj.id === globaledata[i].id && currentObj.localId === globaledata[i].localId) {
                                        console.log(globaledata[i], 'delete pen data');
                                        globaledata.splice(i, 1);
                                        break;
                                    }
                                }
                                drawCanvas.deleteDrawingData(currentObj); // delete it from main collection on deleted from server
                            } else {
                                currentObj.id = response.bookUgcList[0].ugcList[0].id; /*update new Id with existing Id*/
                                drawCanvas.addDrawingData(currentObj); // update id of server if new created
                            }
                        }
                    });
                }
            } else if(Object.keys(currentObj).length){
                /**
                 * Test pen without service calls
                 */
                if (drawCanvas.defaults.mode === 'eraser') {
                    for (var i = globaledata.length - 1; i >= 0; i--) {
                        if (currentObj.pageId === globaledata[i].pageId && currentObj.id === globaledata[i].id && currentObj.localId === globaledata[i].localId) {
                            globaledata.splice(i, 1);
                            break;
                        }
                    }
                    drawCanvas.deleteDrawingData(currentObj); // delete it from main collection on deleted from server
                } else {
                    drawCanvas.addDrawingData(currentObj); // update id of server if new created
                }
            }
            console.log(currentObj, 'currentDrawingObj');
        });
        /* when in 2 page mode always assign left if availabel*/
        drawLeftCanvas = new DrawingCanvas(drawingOptions);
        /**
         * if mouse down on left page
         * @return {object} event
         * @return {object} event.canvas: current mouse up canvas
         * @return {object} event.data: current mouse up position
         * @return {object} event.target: parent or this
  **/
        drawLeftCanvas.on('mouse:down', function(event){
            console.log(event, 'drawLeftCanvas mouse:down');
            drawLeftCanvas.tools[drawLeftCanvas.defaults.mode](event.target, event.data);
        });
        /**
         * if mouse move on left page
         * @return {object} event
         * @return {object} event.canvas: current mouse up canvas
         * @return {object} event.data: current mouse up position
         * @return {object} event.target: parent or this
         **/
        drawLeftCanvas.on('mouse:move', function(event){
            //console.log(event, 'drawLeftCanvas mouse:move');
            drawLeftCanvas.tools[drawLeftCanvas.defaults.mode](event.target, event.data);
        });
        /**
         * if mouse up on left page
         * @return {object} event
         * @return {object} event.canvas: current mouse up canvas
         * @return {object} event.data: current mouse up position
         * @return {object} event.target: parent or this
         **/
        drawLeftCanvas.on('mouse:up', function(event){
console.log(event, 'drawLeftCanvas mouse:up');
            var currentLeftObj = drawLeftCanvas.tools[drawLeftCanvas.defaults.mode](event.target, event.data);;
            ugcBookList = {
"ugcBookList": [{
"bookID": bookId,
"bookVersion": "2.0",
"ugcList": [currentLeftObj]
                }]
            }
            if (isServiceAdapator && Object.keys(currentLeftObj).length) {
                syncManager.syncAdapter.saveUGC(usertoken, ugcBookList, function(response) {
                    if (response.responseCode == 200) {
                        isPenSubmitted = true;
                        if (drawLeftCanvas.defaults.mode === 'eraser') {
                            for (var i = globaledata.length - 1; i >= 0; i--) {
                                if (currentLeftObj.pageId === globaledata[i].pageId && globaledata[i].id === currentLeftObj.id && globaledata[i].localId === currentLeftObj.localId) {
                                    console.log(globaledata[i], 'eraser');
                                    globaledata.splice(i, 1);
                                    break;
                                }
                            }
  drawLeftCanvas.deleteDrawingData(currentLeftObj); // delete it from main collection on deleted from server
                        } else {
                            currentLeftObj.id = response.bookUgcList[0].ugcList[0].id; /*update new Id with existing Id*/
                            drawLeftCanvas.addDrawingData(currentLeftObj); // update id of server if new created
                        }
                    }
                });
            } else if(Object.keys(currentLeftObj).length){
                /**
                 * Test pen without service calls
                 */
                if (drawLeftCanvas.defaults.mode === 'eraser') {
                    for (var i = globaledata.length - 1; i >= 0; i--) {
                        if (currentLeftObj.pageId === globaledata[i].pageId && globaledata[i].id === currentLeftObj.id && globaledata[i].localId === currentLeftObj.localId) {
                            console.log(globaledata[i], 'eraser');
                            globaledata.splice(i, 1);
                            break;
                        }
                    }
                    drawLeftCanvas.deleteDrawingData(currentLeftObj); // delete it from main collection on deleted from server
                } else {
                    drawLeftCanvas.addDrawingData(currentLeftObj); // update id of server if new created
                }
            }
            console.log(currentLeftObj, 'currentDrawingObj');
        });;

Customization

For pen tool, we have to manage pop-ups for pen colors, thickness etc., using defined user controls.

Create view to display pen colors using repeat and on colors click call these two methods

1) setCurrentTool('pen');

2) onColorClick(value) // color value

    /* Function to set tool Pen or Eraser */
           function setCurrentTool(type) {
    var options = {};
    if (type === 'eraser') {
        // resolved IE issue for dynamic cursor
        var eraserCursor = "url('" + basePath + "css/images/eraser.png') 6 24, auto";
        if (BrowserDetect.browser === 'Internet Explorer' || window.navigator.userAgent.indexOf("Edge") > -1) {
            eraserCursor = "url('" + basePath + "css/images/eraser.cur'), auto";
        }

        options = { cursors: eraserCursor } // x, y coo-dinates are also used for delete logic
    } else {
        // resolved IE issue for dynamic cursor
        var penCursor = "url('" + basePath + "css/images/cursor_pen.png') 5 23, auto";
        if (BrowserDetect.browser === 'Internet Explorer' || window.navigator.userAgent.indexOf("Edge") > -1) {
            penCursor = "url('" + basePath + "css/images/cursor_pen.cur'), auto";
        }

        options = { cursors: penCursor }
    }
    if (drawCanvas) {
        drawCanvas.setCurrentTool(type);
        drawCanvas.setDrawOption(options);
    }
    if (drawLeftCanvas) {
        drawLeftCanvas.setCurrentTool(type);
        drawLeftCanvas.setDrawOption(options);
    }
}

function onColorClick(colorElem) {
    $('.pick-colors').removeClass('color-active');
    $(colorElem).addClass('color-active');
    console.log($(colorElem).css("color"), 'onColorClick');
    drawCanvas.setColor($(colorElem).css("color"));
    if (drawLeftCanvas) {
        drawLeftCanvas.setColor($(colorElem).css("color"));
    }
}

// then add all the canvas code mentioned above for enabling Canvas

We need to add all the codes for canvas mentioned above to enable drawing on page and then it will work.

Thumbnail View

Introduction

Kitaboo Sdk exposes a component which helps the user to see a small image representation of pages in the currently opened Book. Here user can navigate to the specific page by clicking on them.

Implementation

/**
             * Custom tocxml.xml created by hurix holds thumbnail data
             * manifest tags inside opf contains information
             */
            if (book.bookdata.manifest.hasOwnProperty('tocxml')) {
Thumbnail implementation can be achieved by using following steps:
// check div show and hide functionality inside togglePopUpContainer function
                // container to hold toc view
                var container = document.getElementById('thumbnailholder');
var resourceContainer = document.getElementById('resourcechild');
                /**
                 * @param {dom} contianer: to hold the thumbanil view dom
                 * manifest tags inside opf contains information of path
                 */
                var thumbnailParams = { container: container };
                book.thumbnailViewManager = new ThumbnailView(thumbnailParams);
                book.thumbnailViewManager.createThumbnailView(book.chapters);

                /*
                 * jump to page. take out page from display number
                 */
                book.thumbnailViewManager.on('thumbnail:itemclick', function(event) {
                    var pageNum = book.displayNum_pageNum_map[event.data.displayNum];
                    render.navigateToPage(parseInt(pageNum));
                    toggleThumbnailView();
                });
                // page history previous
                book.thumbnailViewManager.on('page:history_prev', function(event) {
                    currentPrevPage = book.displayNum_pageNum_map[event.data];
                    render.navigateToPage(currentPrevPage);
                });
                // page history next
                book.thumbnailViewManager.on('page:history_next', function(event) {
                    currentNxtPage = book.displayNum_pageNum_map[event.data];
                    render.navigateToPage(currentNxtPage);
                });
                // navigate to page
  book.thumbnailViewManager.on('page:gotoPage_value', function(event) {
                    var pageNum = document.getElementById('input_value').value;
                    pageNum = book.displayNum_pageNum_map[pageNum];
                    render.navigateToPage(parseInt(pageNum));
                });

Customization

For thumbnail we can customize the look and theme using css

Markup

Introduction

KitabooSdk facilitates a user to add different icons as a "Markup", which makes the user experience more interactive on pages.

KitabooSdk supports the following types of Markup :

        GLOSSARY,
        INTERNAL,
        WEB_ADDRESSES,
        RESOURCE,
        IMAGE,
        VIDEO,
        ACTIVITY,
        ANIMATIONS,
        DOCUMENTS,
        HTML_WRAP,
        KITABOO_WIDGET,
        PDF,
        MULTIPLE_LINK,
        AUDIO,
        AUDIO_SP,
        COMMENTS,
        INPUT,
        BUTTON,
        RADIOBUTTON,
        CHECKBOX,
        LINK,
        QA ACTIVITY,
        AUDIO_SYNC,
        ACTIVITY_INJECTION,
        INTERACTIVE,
        SLIDESHOW,
        YOUTUBE STREAMING,
        KALTURA STREAMING,
        EXTERNAL_IMAGE,
        EXTERNAL_AUDIO,
        EXTERNAL_VIDEO,
        EXTERNAL_WEB_LINK,
        EXTERNAL_DOCUMENTS,
        EXTERNAL_HTML_WRAP,
        STANDALONE_INSTRUCTION,
        VIMEO_VIDEO

Implementation

Use the following code to add Markup to your reader :

/**
         * event for markup's click on page
         * @return {object} markup
         * @return {object} markup.id: markup id which is clicked
         * @return {object} markup.type: type of clicked markup on page.ie., image, video, audio etc
         * @return {object} markup.pageId: id of the page on which resource was clicked
         * @return {object} markup.title: title given to markup if any
         * @return {object} markup.alt: alt given to markup if any
         * @return {object} markup.basePath: path till parent folder
         * @return {object} markup.extra: extra information related to the markup
         **/
        render.on('markup:clicked', function(markup) {
            console.log(markup, 'markup:clicked');
            openMarkup(markup);
        });

/*
 * check the markup and open accordingly
 */
function openMarkup(markup) {
    closeAllPopup();

    var type = markup.type;
    switch (type) {
        case 'documents': 
          var blob = markup.markupUrl
          if (blob.indexOf('.pdf') > 0) {
              var appRootURL = window.location.href.substring(0, window.location.href.indexOf('#'));
                  appRootURL = appRootURL || window.location.href;
                  appRootURL = appRootURL.substring(0, appRootURL.lastIndexOf('/') + 1);
              if ((BrowserDetect.browser === 'Internet Explorer') || (window.navigator.appName == "Netscape"&& window.navigator.appVersion.indexOf('Edge') > -1)) {
                  var docViewer = window.open(appRootURL + 'views/docViewer.html');
                  if (docViewer.attachEvent) {
                      docViewer.attachEvent('onload', pdfOnLoad(docViewer, blob));
                  } else if (docViewer.addEventListener) {
                      docViewer.addEventListener('load', pdfOnLoad(docViewer, blob), false);
                  } else {
                      docViewer.document.addEventListener('load', pdfOnLoad(docViewer, blob), false);
                  }
              } else {
                  var docViewer = window.open(appRootURL+ 'views/docViewer.html');
                  docViewer.onload = function() {
                      if (docViewer.document.getElementsByTagName('iframe') !== undefined) {
                          docViewer.document.getElementsByTagName('iframe')[0].src = blob
                      } else {
                          var ifrm = document.createElement("iframe");
                              ifrm.setAttribute("src", blob);
                          docViewer.document.body.append(ifrm);
                      }
                  };
              }
          } else {
              window.open(blob);
          }
        break;
        case 'audio':
          $('.markup .container').draggable({
            iframeFix: true,
            handle:'.drag-handler-icon',
            containment: ".epub-viewer"
          });
          $('.markup .container').find('audio')[0].src = markup.markupUrl;
          $('.markup .container').find('audio')[0].autoplay = true;
          $('.markup').show();
      break;
        case 'htmlwrap':
                /* initiliazed prettyPhoto for */
                $().prettyPhoto({
                    social_tools: false, deeplinking: false, opacity: 0.2,
                    type: markup.type, caption: markup.title});
                /* type htmlwrap */
                $.prettyPhoto.open(markup.basePath+ markup.markupUrl, '', markup.title);
            break;
        case 'kaltura_video':
//Use Auth and signed the URL
// markup object contains all the information below are the properties
/**
 * {object} markup
 * {object} markup.id: markup id which is clicked
 * {object} markup.type: type of clicked markup on page.ie., image, video, audio etc
 * {object} markup.pageId: id of the page on which resource was clicked
* {object} markup.title: title given to markup if any
* {object} markup.alt: alt given to markup if any
* {object} markup.markupUrl: Url for the mark
* {object} markup.basePath: path till parent folder
* {object} markup.extra: extra information related to the markup
**/
// Once you get signed url for Kaltura after Auth request, open it using following code 
/* type kaltura_video
            $().prettyPhoto({social_tools: false, deeplinking: false,
                opacity: 0.2, allow_resize: true, type: markup.type})
            $.prettyPhoto.open(singed_url, '', markup.title);*/
            break;
case 'audioSync':
// audio implementation is integrated inside shell/js/custom_audio_player.js
            render.loadAndParseCuePoints(markup.cuePointsXMLPath, function(data) {
                custom_audio.addSource(markup.markupUrl);
                $('.markup .container').draggable({
                    iframeFix: true,
                    handle: '.drag-handler-icon',
                    containment: ".epub-viewer"
                });
                $('.markup').show();
                var target = render.getAudioSyncSpan(markup); // get the span which will highlight the word
                custom_audio.bind('play', function() {
                    trackCurrentTime.call(this.sound); // playing audio object
                });
                /* highlight the sync word on time update*/
                function trackCurrentTime() {
                    var currentTime = this.currentTime;
                    for (var i = 0; i < data.length; i++) {
                        if ((currentTime >= data[i].start && currentTime <= data[i].end)) {
                            $(target).show().css({
                                width: data[i].width,
                                height: data[i].height,
                                left: data[i].x,
                                top: data[i].y
                            });
                        }
                    }
                    requestAnimFrame(trackCurrentTime.bind(this)); // rather then using interval use window.requestAnimationFrame
                }
            });
            break;
        default:
            /* type image, video */
            $().prettyPhoto({social_tools: false, deeplinking: false,
                opacity: 0.2, allow_resize: true, type: markup.type})
            $.prettyPhoto.open(markup.markupUrl, '', markup.title);
        break;
    }
}
/**
 * PDF loaded event
 */
function pdfOnLoad(docViewer,blob){
     window.setTimeout(function() {
        docViewer.pdfLoad(blob);
    }, 500);   
}
/**
 * close the audio markup
 */
function closeMarkup(){
  $('.markup').find('audio')[0].pause();
  $('.markup').find('audio')[0].currentTime = 0;
  $('.markup').hide();
}
function closeAllPopup(){
  $('.thumb-nail').removeClass('active');
  $('.thumbnail-holder').hide();
  closeDrawingContainer();
  togglePopUpContainer();
  closeMarkup();
}

Customization

  • We can customize markup by using different Icons in resource tab as well as page.
  • We can also provide custom popup for all markup and open it differently in cases below :

Example

We have created a different popup for customizing the view of video popup and we are using a different library to play the video. So, you can see the sample code below to launch the video in different popup in case of video.

function openMarkup(markup) {
    closeAllPopup();

    var type = markup.type;
    switch (type) {
      case 'video':
           openMP4VideoMarkup(markup);
           break;
}

function openMP4VideoMarkup(markup) {
        var fileType = markup.markupUrl.substring(markup.markupUrl.lastIndexOf('.') + 1, markup.markupUrl.length); // return file type ex: mp4, webm, ogg
        var videoTitle = markup.title; // video title

        // object that contains media information
      var media = {
            sources: [{
                src: markup.markupUrl,
                type: 'video/' + fileType,
            }],
            tracks: markup.subtitle ? [{
                kind: 'subtitles',
                label: 'English subtitles',
                src: '',
                srclang: 'en',
                default: true
            }] : [],
        };

        var infoObj = {
            templateUrl:<videoPlayerView>
        };

       openModalPopup('$event', URL, 'parent');
    };

Service Adaptor

Introduction

  • To use the Kitaboo services, "Kitaboo SDK" provides an adapter called "syncManager.syncAdapter" which uses Kitaboo services and supports the required API calls [ ie; Fetch/Save UGC request and others].
  • Service triggering from Reader : services can be triggered from Reader on specific event.

Example

We are triggering the "FetchUGC" request while opening the book. Similarly, another Service API can be triggered based on the requirement at the Reader end.

Service Adaptor used to call the services to save data to server, login etc.,

Use the following code to add Service Adaptor to your project :

/**
         * service manager implementation
         **/
        // code to extent ServiceAdapter default function
        /*var kitabooadapter = function() {};
            kitabooadapter.prototype = Object.create(ServiceAdapater);
            kitabooadapter.prototype.login = function(usertocke) {}*/
        var syncManager = new SyncManager(ServiceAdapater);
            syncManager.syncAdapter.setHost('https://qc.kitaboo.com'); // default setting to qc
/**
         * uncomment below code if already login and have BookID
         * comment code for login and getUserBookList service
         */
        // syncManager.syncAdapter.setUserToken(sessionStorage.getItem('cloudUserToken'));
      // syncManager.syncAdapter.setBookID(sessionStorage.getItem("cloudBookId"));
        //syncManager.syncAdapter.setLastPageFolioIdID(sessionStorage.getItem('lastPageFolio'));
        /*var userVO = {};
            userVO.firstName = sessionStorage.getItem("cloudFirstName");
            userVO.lastName = sessionStorage.getItem("cloudLastName");
            userVO.userRole = sessionStorage.getItem("cloudRoleName");
            userVO.userID = sessionStorage.getItem("cloudUserId");
            userVO.userName = sessionStorage.getItem("cloudUserName");

        syncManager.syncAdapter.setUserVO(userVO);*/        
/**
         * calling login service and getting userToken
         * set userToken
         * @param {string} userid
         * @param {string} password
         * @retun {string} userToken
         **/
        syncManager.syncAdapter.login('fib35@yopmail.com', 'kitaboo@123',function(response){
            syncManager.syncAdapter.setUserToken(response.userToken);
                    var userVO = {};
                        userVO.firstName = response.user.firstName;
                        userVO.lastName = response.user.lastName;
                        userVO.userRole = response.user.roles[0].name;
                        userVO.userID = response.user.userID;
                        userVO.userName = response.user.userName;

                    syncManager.syncAdapter.setUserVO(userVO);
                    username = syncManager.syncAdapter.userVO.userName;
                    userRole = syncManager.syncAdapter.userVO.userRole;
                    usertoken = syncManager.syncAdapter.userToken;
            /**
             * fetch user booklist from server
             * @param {string} userToken
             * @return {[]} return booklist
             */
            syncManager.syncAdapter.getUserBookList(usertoken, function(response){
                syncManager.syncAdapter.setBookID(response.bookList[0].book.id);
 syncManager.syncAdapter.setLastPageFolioIdID(response.bookList[0].book.classList[0].lastPageFolio);
bookId = syncManager.syncAdapter.bookid;
                            lastPageFolioId = syncManager.syncAdapter.lastPageFolioId;

                /*
                 * get either shared or received data for validate user
                 * @param {string} bookId
                 * @param {string} usertoken
                 * @param {number} timestamp
                 */
                syncManager.syncAdapter.fetchBookClassInfo(bookId, usertoken, function(response){
                    settingData = response;
                });

                /** 
                 * fetch UGC data from server
                 * @param {string} bookId
                 * @param {string} usertoken
                 * @param {number} timestamp
                 */
                syncManager.syncAdapter.fetchUGC(bookId, usertoken, Math.round(new Date().getTime()/1000), function(response){
                    if(response.responseCode == 200){
                        var ugcList = response.bookUgcList[0].ugcList;
                                    for (var i = 0; i < ugcList.length; i++) {
                                        var currentUGCVO = ugcList[i];
                                        if ((currentUGCVO.type === 1 || currentUGCVO.type === 2) && currentUGCVO.status != 'DELETE') {

                                            var isUgcData = currentUGCVO.hasOwnProperty('ugcData')
                                            var ugcData = currentUGCVO.ugcData;

                                            var metadata = JSON.parse(decodeURIComponent(currentUGCVO.metadata));
                                            var isImp = (currentUGCVO.important === 'Y') ? true : false;
                                            // color property not found in metadata on server
                                            if(!metadata.hasOwnProperty('color')){
                                                if(isImp){
                                                    metadata.color = '#FF0000';
                                                }else{
                                                    metadata.color = '#FFFF00';
                                                }
                                                currentUGCVO.metadata = ObjectToEcodeURI(metadata);
                                            }
                                        }else if(currentUGCVO.type === 4  ){
                                            bookmarkobj.bookmarkManager.listofbookmarks.push(currentUGCVO);
                                        }
                                        renderUGCDataOnPage(currentUGCVO);
                                        globaledata.push(currentUGCVO);// set user UGC data. Notes, Highlight etc.,
                                    }
                    }
                });
            });
        });

Fit Height, Fit Width & Zoom

Introduction

Pages by default fit-to-height. A user can zoom the page as well as make a page to its width.

Implementation

Add the following code to your reader :

/**
 * fit the page to width or height
 **/
function fitWidthHeight(elem) {
    highlightEnable = false;
    closeAllPopup();
    lastopen = '';
    $(elem).toggleClass('fith-icon');
    render.fitPage(); // method to fit the page
}
/**
* call this on zoom buttom
 * zoomin zoomout the page
 **/
function zoomInOut(elem) {
    highlightEnable = false;
    closeAllPopup();
    lastopen = '';
    $(elem).toggleClass('zoom-out-icon');
    $('.zoom-slider-container').animate({ width: 'toggle' }, 500);
}
/**
* function called when slider change triggers
     * zoom slider
     */
        $(".zoomSlider").slider({
            min: min_scale,
            max: max_scale,
            animate: true,
            step: 0.1,
            slide: function(event, ui) {
                render.zoomBy(ui.value); // send the zoom value to scale the page ie., 0.3
                console.log(ui.value, 'zoomSlider');
            }
        });

Single Page and Double Page View

Introduction

KitabooSdk facilitates Single Page and Double Page view as well to enhance Readability and User Experience

Implementation

Use the following code for your reader :

setPageMode needs boolean value to switch the view in single or double page so it you pass true it will render double page and vice versa

render.setPageMode(isDouble)

Dictionary

Introduction

Dictionary implementation can be achieved by following the below mentioned steps :

  1. Add the dictionary icon in highlight pop-view at the reader's end
  2. Get Highlighted text-related data from Dictionary
  3. Handling click event of Dictionary icon

Implementation

Adding dictionary icon in highlight pop view :

/**
         * Highlight and Note contextual menu configuration object
         * @param {object} config
         * @param {string} [config.textSelectionCSS] : 
         */
        var config = {
            textSelectionCSS: "color:white; background:red",
            defaultHighlighCSS: "border-radius: 23px;opacity: 1.0;padding-left: 5px;padding-right: 5px;background-color: rgba(0, 0, 0, 0.6) !important;display:none;height:50px;",
            stickynotetext: 'S',
            contextualnoteicon: 'V'
        }

        // create highlight class object
        var highlight = new KitabooHighlight(config);
/**
         * Define the item that will shown in context menu for document selection
         * eg., highlight, notes
         * refer item1 below
         * @properties {object.icon}: icon or placeholder for item
         * @properties {object.id}: unique id which represents item
         * @properties {object.css}: background-color, position of item in content menu
         */
        var item1 = {
            icon: "e",
            id: 100,
            css: "background: transparent;border: 0px;float: left;width: 50px;color: yellow; font-size: 1.5em; font-family: kitabooread;"
        }
// add the create object in highlight object. when user selects text on document. 
        // Popup will contain the above custom object(item1, item2)
        highlight.addItem(item1);

Get the Highlighted text related data from Dictionary :

// once DOM selection complete. show popup for editing
        render.on("highlight:section", function(textobject, page) {
            highlight.show(textobject, page);
        });

Handling click event of Dictionary icon :

/*
         * Event dispatched on custom item clicked in context menu
         * Custom item create by user and information
         * Using which he/she can manipulate the selection on DOM
         */
        highlight.on("highlight:onitclemClick", function(highlightobject) {
if (highlightobject.item.id == '100') {
// highlightobject.item.id: unique id given by user when created highlight 
// dictionary implementation will go here
}
});