Jump To …

background.js

/*jshint indent:2 */
/*global chrome:true */

This is the background-script that stays alive all the time, the dialog and the menu are connected to this. All main interactions, with the api or with the browser-ui are here. The communication with the menu is done via Port (messaging object from chrome) the cite-popup is handled with a single message that send, but for this we need to use a kind of a messageQueue to handle the awkful nature of the chrome-api (missing right events to interact with an created window)

(function() {

    'use strict';

    var debug, messageQueue, checkIntervalID, easyUtils, EasyBibUtils;

polyfill

    if (typeof String.prototype.startsWith !== 'function') {

see below for better implementation!

        String.prototype.startsWith = function(str) {
            return this.indexOf(str) === 0;
        };
    }

wrapping console.logs for debug purposes

    debug = {
        active: true,
        log: function() {
            if (debug.active) {
                console.log.apply(console, arguments);
            }
        }
    };

MessageQueue, which is needed to communicate with the cite-popup, so message is send when the tab is complete loaded

    messageQueue = [];

    function checkMessageQueue() {
        var msg;
        if (messageQueue.length === 0) {
            return;
        }


        while (msg = messageQueue.pop()) {
            var mymsg = msg; //cause gets undefined is empty - async nature of get tabs
            chrome.tabs.get(msg.tabId, function(tab) {
                if (tab.status !== 'complete') {
                    messageQueue.push(mymsg);
                } else {
                    if (tab.url.toString().startsWith('chrome-')) {
                        chrome.tabs.sendMessage(mymsg.tabId, {
                            type: mymsg.type,
                            url: mymsg.url,
                            params: mymsg.params
                        });
                    }
                }
            });
        }
    }
    checkIntervalID = window.setInterval(checkMessageQueue, 100);


    EasyBibUtils = function() {};
    EasyBibUtils.prototype = {
        timeout: 4000, // standard - timeout for $.ajax-calls

urls that are used by different functions

        url: {
            search: 'http://research.easybib.com/research/index/search?utm_campaign=toolbar&utm_source=chrome&search=',
            bibliography: 'http://www.easybib.com/cite/view?utm_source=chrome&utm_campaign=toolbar',
            bibliographyEntry: 'http://www.easybib.com/cite/json?list=',
            autocite: 'https://autocite.citation-api.com/index/json',
            citeform: 'http://www.easybib.com/mla-format/website-citation?toolbar=true',
            newcitation:'http://easybib.com/rest/bulkadd',
            credibility: 'http://www.easybib.com/credibility/index/json',
            notebook: 'http://www.easybib.com/notebook/api/Helper_Bulk.send',
            home: 'http://www.easybib.com/?utm_source=chrome&utm_campaign=toolbar',
            profile: 'http://www.easybib.com/account/profile',
            loggedin: 'http://www.easybib.com/account/session',
            projectlist: 'http://www.easybib.com/folders/api/projects',
            change_active_project: 'http://www.easybib.com/cite/view/list/' // this + the project id would change the active project

projectlist: 'http://v5.easybib.com/list/lists'

        },

this vars are used to cache the data, they will be reseted, if the user is clicking the refresh button

        login: false,
        projectlist: [],
        openedFirsttime: true,

Utility function that sets the url of the active tab in the active window. This function is used to navigate to a specific url (easybib, bibliography, search)

        openUrl: function(url) {
            var df = new $.Deferred();
            chrome.windows.getCurrent(function(currentWindow) {
                chrome.tabs.create({
                    url: url
                }, function() {
                    df.resolve(true);
                });
            });
            return df;
        },

This function checks the credibility of the url of the active tab in the active window. @returns Deferred-Object (done: yes, may, no | fail: errormsg)

        checkCredibility: function(checkurl) {
            var df = new $.Deferred(),
                self = this;

            $.ajax({
                url: self.url.credibility,
                type: 'GET',
                dataType: 'JSON',
                timeout: self.timeout,
                data: {
                    url: checkurl
                }
            }).done(function(response) {
                var not_eval_msg = 'Source is not evaluated. ';
                not_eval_msg += '<a href="http://content.easybib.com/students/research-guide/';
                not_eval_msg += 'website-credibility-evaluation/" target="_blank" class="evaluate">Evaluate it</a>!';

                if (response.status === 'error') {
                    df.reject(not_eval_msg);
                }

                if (response.status === 'not_found') {
                    df.reject(not_eval_msg);
                }

                if (response.status === 'ok') {
                    if ((/yes/i).test(response.info.credible)) {
                        df.resolve('yes');
                    } else if ((/maybe/i).test(response.info.credible)) {
                        df.resolve('may');
                    } else {
                        df.resolve('no');
                    }
                }
            }).fail(function(err) {
                df.reject('Credibility could not be determined.');
            });
            return df;
        },

        getCitationID: function(projectID, currentPageURL){
          var df = new $.Deferred(),
              self = this,
              citationExists = false;
          $.ajax({
              url: self.url.bibliographyEntry,
              type: 'GET',
              timeout: self.timeout,
              data: projectID
          }).done(function(response) {
              var documentID;
              $.each(JSON.parse(response).data, function(index, bibliographyEntry) {
                if ( bibliographyEntry.doc.pubonline ) {
                  if ( bibliographyEntry.doc.pubonline.url === currentPageURL) {
                    documentID = bibliographyEntry.doc._id;
                  }
                }
              });
              df.resolve(documentID);
          }).fail(function(err) {
              df.reject('Couldnt get document id for ' + currentPageURL);
          });
          return df;
        },

This function calls autocite to generate bibliography data @returns Deferred-Object (done: yes, may, no | fail: errormsg)

        createNewCitation: function(autociteData, projectID) {
            var df = new $.Deferred(),
                self = this;
            autociteData.source = "website";
            autociteData.listid = projectID;
            $.ajax({
                url: self.url.newcitation,
                type: 'POST',
                dataType: 'JSON',
                timeout: self.timeout,
                data: {'docs': [autociteData]}
            }).done(function(response) {
                df.resolve(response);
            }).fail(function(err) {
                df.reject('Could not call autocite.');
            });
            return df;
        },

This function calls autocite to generate bibliography data @returns Deferred-Object (done: yes, may, no | fail: errormsg)

        callAutocite: function(currentPageURL, projectID) {
            var df = new $.Deferred(),
                self = this;
            $.ajax({
                url: self.url.autocite + '?url=' + currentPageURL,
                type: 'GET',
                timeout: self.timeout

data: currentPageURL

            }).done(function(response) {
                self.createNewCitation(response.data.data, projectID).then(function(resp) {
                    df.resolve(resp);
                });
            }).fail(function(err) {
                df.reject('Could not call autocite.');
            });
            return df;
        },

This function creates a new bibliography entry though it can be called on it's own, it's intended to be used in conjunction with functions that create new notes. @returns Deferred-Object (done: yes, may, no | fail: errormsg)

        checkIfWebCitationExists: function(projectID, currentPageURL) {
            var df = new $.Deferred(),
                self = this,
                documentID = null;

            $.ajax({
                url: self.url.bibliographyEntry,
                type: 'GET',

dataType: 'JSON',

                timeout: self.timeout,
                data: projectID
            }).done(function(response) {
                var documentID;
                $.each(JSON.parse(response).data, function(index, bibliographyEntry) {
                  if ( bibliographyEntry.doc.pubonline ) {
                    if ( bibliographyEntry.doc.pubonline.url === currentPageURL) {
                      documentID = bibliographyEntry.doc._id;
                      df.resolve(documentID);
                    }
                  }
                });
                if (df.state() !== 'resolved'){
                  self.callAutocite(currentPageURL, projectID).done(function(result){
                    if (result.status == 'ok') {
                        var message = JSON.parse(result.message);
                        documentID = message[0].id;
                        df.resolve(documentID);
                    } else {
                        df.reject('Citation not created.');
                    }
                  });
                }
            }).fail(function(err) {
                df.reject('Could not create new note.');
            });
            return df;
        },

This function submits the contents of the clipboard to the notebook specified in the UI of the extension @returns Deferred-Object (done: yes, may, no | fail: errormsg)

        addToNotebook: function(content, projectID) {
            var df = new $.Deferred(),
                self = this;

            var randomSevenDigitNum = Math.floor(Math.random() * 9000000) + 1000000;
            var randomTenDigitNum = (+new Date() + "").substr(0, 10);
            var randomID = randomSevenDigitNum + '-' + randomTenDigitNum;

var notecardTitle = content.evidence.substr(0, 20);

            var notecardTitle = content.title || content.evidence.substr(0, 20);
            var notecardLeft = 120;
            var notecardTop = 100;

            var randomInterval = (Math.floor(Math.random() * 10) + 10);
            var leftPosition = randomInterval + notecardLeft + 'px';
            var topPosition = randomInterval + notecardTop + 'px';

            var currentTimestamp = Date.now();

The nomenclature for the note fields and their actual labels in the form starts to get hairy at this point.

            var noteData = {
                document: [{
                    "method": "Note.save",
                    "id": randomID,
                    "document": {
                        "color": "rgb(235, 235, 235)",
                        "id": randomID,
                        "title": notecardTitle,
                        "quote": content.evidence,
                        "paraphrasing": content.paraphrasing,
                        "source": null,
                        "url": "",
                        "left": leftPosition,
                        "top": topPosition,
                        "content": content.comment,
                        "type": "Note",
                        "timestamp": currentTimestamp,
                        "created_by": easyUtils.login.id
                    }
                }],
                "listId": projectID
            };

            self.checkIfWebCitationExists(projectID, content.currentPageURL).then(function(result) {
                noteData.document[0].document.source = result;
                $.ajax({
                    url: self.url.notebook,
                    type: 'POST',
                    dataType: 'JSON',
                    timeout: self.timeout,
                    data: $.param(noteData)
                }).done(function(response) {
                    df.resolve(true);
                }).fail(function(err) {
                    df.reject('Could not create new note.');
                });
            });


            return df;
        },

Collects Autocite - Data, if possible and then opens the citation-form on easybib.com

        citeOnEasyBib: function(citeurl) {
            var self = this;
            self.getCiteData(citeurl)
                .done(function(data) {
                    self.openCiteOnEasyBib(data);
                })
                .fail(function(err) {
                    self.openCiteOnEasyBib({});
                });
        },

gets autocite data for url in the active tab in the active window @returns Deferred-Object

        getCiteData: function(citeurl) {
            var df = new $.Deferred(),
                self = this;

            chrome.windows.getCurrent(function(currentWindow) {
                chrome.tabs.getSelected(currentWindow.id, function(selectedTab) {
                    $.ajax({
                        url: self.url.autocite,
                        type: 'GET',
                        dataType: 'JSON',
                        timeout: self.timeout,
                        data: {
                            url: citeurl
                        }
                    }).done(function(resp) {
                        if (resp.status === 'ok') {
                            df.resolve(resp.data.data);
                        } else {
                            df.reject(resp);
                        }
                    }).fail(function(err) {
                        df.reject(err);
                    });
                });
            });
            return df;
        },

Opens cite-dialog. Works a strange, cause the api is expecting an json-post this is realized by generating a form in the new window (dialog_popup.js) and submitting it to the apis-endpoint.

        openCiteOnEasyBib: function(data) {
            var df = new $.Deferred(),
                self = this;
            chrome.windows.create({
                type: 'popup',
                url: 'dialog.html',
                width: 590,
                height: 550
            }, function(nWindow) {
                var tabInfo = nWindow.tabs[0],
                    message = {
                        tabId: tabInfo.id,
                        type: 'send_postdata',
                        url: self.url.citeform,
                        params: data
                    };
                messageQueue.push(message);

            });
        },

check if the user is logged into easybib

        getLoggedInStatus: function() {
            var df = new $.Deferred(),
                self = this;

just reset the cache

            easyUtils.login = false;

            $.ajax({
                type: 'GET',
                accepts: {
                    json: 'application/json'
                },
                dataType: 'json',
                url: self.url.loggedin
            })
                .done(function(res) {
                    if (res.error) {
                        easyUtils.login = false;
                        df.resolve(false);
                    } else {
                        easyUtils.login = res.data;
                        df.resolve(res.data);
                    }
                })
                .fail(function(err, statusText) {
                    easyUtils.login = false;
                    if (statusText === 'parsererror') {
                        df.resolve(false); //for now its a parse error if no session is fount
                    } else {
                        df.reject(err);
                    }
                });

            return df;
        },

        getProjectList: function() {
            var df = new $.Deferred(),
                self = this;

just reset the cache

            easyUtils.projectlist = [];

            $.ajax({
                type: 'GET',
                accepts: {
                    json: 'application/json'
                },
                dataType: 'json',
                url: self.url.projectlist
            })
                .done(function(res) {
                    easyUtils.projectlist = res;
                    df.resolve(res);
                })
                .fail(function(err, statusText) {
                    easyUtils.projectlist = [];
                    if (statusText === 'parseerror') {

not logged in

                        df.resolve(false);
                    } else {
                        df.reject(err);
                    }
                });

            return df;
        },

        changeActiveProject: function(projectid) {
            var df = new $.Deferred(),
                self = this;

            $.ajax({
                type: 'GET',
                dataType: 'html',
                url: self.url.change_active_project + projectid
            })
                .done(function(res) {

ok we think its done

                    df.resolve(true);
                })
                .fail(function(err) {

something went wrong

                    df.reject('something went wrong');
                });

            return df;
        }
    };

delegating messages

    easyUtils = new EasyBibUtils();
    window.easyUtils = easyUtils;
    window.$ = $;

    chrome.extension.onConnect.addListener(function(port) {
        port.onMessage.addListener(function(msg) {
            if (msg.type === 'cite') {
                easyUtils.citeOnEasyBib(msg.param);
            } else if (msg.type === 'goeasybib') {
                easyUtils.openUrl(easyUtils.url.home);
            } else if (msg.type === 'gonotebook') {
                easyUtils.addToNotebook(msg.content, msg.projectID)
                    .done(function() {
                        debug.log('done!');
                        port.postMessage({
                            type: 'done-creating-note',
                            state: 'done'
                        });
                    })
                    .fail(function() {
                        port.postMessage({
                            type: 'done-creating-note'
                        });
                        debug.log('note creation failed! :(');
                    });
            } else if (msg.type === 'gobibliography') {
                easyUtils.openUrl(easyUtils.url.bibliography);
            } else if (msg.type === 'search') {
                easyUtils.openUrl(easyUtils.url.search + msg.param);
            } else if (msg.type === 'check-credibility') {
                easyUtils.checkCredibility(msg.param)
                    .done(function(res) {
                        port.postMessage({
                            type: 'update-credibility',
                            state: 'done',
                            res: res
                        });
                    })
                    .fail(function(msg) {
                        port.postMessage({
                            type: 'update-credibility',
                            state: 'fail',
                            text: msg
                        });
                    });
            } else if (msg.type === 'check-loginstatus') {
                easyUtils.getLoggedInStatus()
                    .done(function(res) {
                        port.postMessage({
                            type: 'update-loginstatus',
                            state: 'done',
                            res: res
                        });
                    })
                    .fail(function(err) {
                        port.postMessage({
                            type: 'update-loginstatus',
                            state: 'fail',
                            res: err
                        });
                    });
            } else if (msg.type === 'get-projectlist') {
                easyUtils.getProjectList()
                    .done(function(res) {
                        port.postMessage({
                            type: 'update-projectlist',
                            state: 'done',
                            res: res
                        });
                    })
                    .fail(function(err) {
                        port.postMessage({
                            type: 'update-projectlist',
                            state: 'fail',
                            res: err
                        });
                    });
            } else if (msg.type === 'popup-opened') {

the popup is opened, so look into the cache and fire status if there is any other than logged out out of the cache

                var loginStatus = easyUtils.login;

                port.postMessage({
                    type: 'update-loginstatus',
                    state: 'done',
                    res: loginStatus
                });

                port.postMessage({
                    type: 'check-textselection'
                });

                if (easyUtils.login !== false) {
                    if (easyUtils.projectlist && easyUtils.projectlist.length > 0) {

out of the cache

                        port.postMessage({
                            type: 'update-projectlist',
                            state: 'done',
                            res: easyUtils.projectlist
                        });
                    }

                } else if (easyUtils.openedFirsttime) {

if the popup is opened the first time then login status is really checked and setted after this only the refresh button would trigger this action again

                    easyUtils.openedFirsttime = false;
                    easyUtils.getLoggedInStatus()
                        .done(function(res) {
                            port.postMessage({
                                type: 'update-loginstatus',
                                state: 'done',
                                res: res
                            });
                        })
                        .fail(function(err) {
                            port.postMessage({
                                type: 'update-loginstatus',
                                state: 'fail',
                                res: err
                            });
                        });
                } else {
                    throw new Error('obviously opened not the first time');
                }

            } else if (msg.type === 'change-active-project') {
                easyUtils.changeActiveProject(msg.project_id);
            }

        });
    });

adding webRequest listener to trigger login-status updates if the user loads /logout successfully that gets redirected by the server

    chrome.webRequest.onBeforeRedirect.addListener(function(details) {
        easyUtils.getLoggedInStatus()
            .always(function() {

reset the cached project list

                easyUtils.projectlist = [];
            });
    }, {
        urls: ['*://*.easybib.com/logout']
    });

adding webRequest listener to trigger login-status updates if the user successfully logs in and loads the project list view

    chrome.webRequest.onCompleted.addListener(function(details) {
        easyUtils.getLoggedInStatus()
            .always(function() {

reset the cached project list

                easyUtils.projectlist = [];
            });
    }, {
        urls: ['*://*.easybib.com/list']
    });

    function launchPopUp() {
        return function(info, tab) {
              var selectionText = info.selectionText;
              var url = "../context_menu_support/modal.html";
              var popUpHeight = 560;
              var popUpWidth = 800;
              chrome.windows.create({
                  type: 'popup',
                  url: url,
                  height: popUpHeight,
                  width: popUpWidth,
                  left: screen.width / 2 - (popUpHeight / 2),
                  top: screen.height / 2 - (popUpWidth / 2)
              });
        };
    }
}());