aboutsummaryrefslogblamecommitdiffstats
path: root/importAddrBook.js
blob: 42322ee8edf4486f7e23c7e46dbb7f678512a004 (plain) (tree)
1
2
3
4
5
6
7
8
9
10



                                                                      
                            

                                              


               






































                                                                          

                                   












                                                                     
                                           



                                         


                           





                                                        


                             











                                                     


                             
















                                                                     

                                               







                                                    

                                              




                                         

                                               

                             

                                               

                                

                                                 

                                  

                                                 

                                                  

                                               

                                                   

                                               

                                                 

                                               

                                            

                                               

                                             

                                               

                                          

                                               










                                           

                                               

                                  

                                               

                                           

                                               










                                                         














                                                                        





                                          

   
 









































                                                                   







                                                                   
                                 



                                                                      
                                                      























                                                                           



                                                             


                                      

                                                                



          

   


                                                               
 

                                                               
 


                                                                      
 





                                      
 



                                    
 


                                               
 

       
 



                                               
 




                          
 




                                                          
 

                                            


     
/*jshint forin:true, noarg:true, noempty:true, eqeqeq:true,
  bitwise:true, strict:true, undef:true, curly:true, browser:true,
  devel:true, indent:2, maxerr:50, moz:true, newcap:false, moz:true */

/* global mozContact: false,
   ContactTelField: false, parseLDIF: false */

(function () {
  "use strict";

  // Shim for the intersection of two Arrays.
  if (!Array.prototype.intersection) {
    Array.prototype.intersection = function (anotherArr) {
      return this.filter(function(n) {
        if (anotherArr.indexOf(n) === -1) { return false; }
        return true;
      });
    };
  }

  const phoneFields = [
      "mobile", "facsimiletelephonenumber", "homePhone", "telephoneNumber"
    ];

  const emailFields = [ "mail" ];

  function ValueError(message) {
    this.name = "ValueError";
    this.message = message || "Unknown Value";
  }
  ValueError.prototype = new Error();
  ValueError.prototype.constructor = ValueError;

  /**
   * Translate objects created from LDIF files to Contacts
   *
   * @param Array of LDIF objects
   * @return Contact with all fields filled
   * @throws ValueError for unknown field in the LDIF object
   *
   * ContactsAPI:
   * @see https://wiki.mozilla.org/WebAPI/ContactsAPI
   * @spec http://www.w3.org/TR/contacts-manager-api/
   *
   * Thunderbid LDAP Schema:
   * @see https://developer.mozilla.org/docs/Thunderbird/LDAP_Support
   * @see https://tools.ietf.org/html/rfc4519
   */
  function translateObjectToContact(inRec) {
    var contact = {},
        pomStr = "", curRec = null;

    /**
     * Find the proper record (or create new one) in the multi-value
     * attribute
     *
     * @param idx name of the attribute to which the record belongs
     * @param subClass Function constructor of the record if there is
     * none
     * @param subType String with the type of the record
     * @return record of the proper class
     *
     * Uses global variable contact.
     */
    function findSubElement(idx, subType) {
      var cAddr = null;

      // No contact.adr at all
      if (!contact.hasOwnProperty(idx)) {
        cAddr = {
          'type': [subType]
        };
        contact[idx] = cAddr;
      }
      // Single-element property
      else if (!Array.isArray(contact[idx])) {
        if (contact[idx].type.indexOf(subType) === -1) {
          contact[idx] = [contact[idx]];
          cAddr = {
            'type': [subType]
          };
          contact[idx].push(cAddr);
        }
        else {
          cAddr = contact[idx];
        }
      }
      // Array
      else {
        cAddr = contact[idx].filter(function (addr) {
          return addr.type.indexOf(subType) !== -1;
        });
        if (cAddr.length === 0) {
          cAddr = {
            'type': [subType]
          };
          contact[idx].push(cAddr);
        }
        else {
          cAddr = cAddr[0];
        }
      }

      return cAddr;
    }

    for (var key in inRec) {
      if (key === "birthyear") {
        pomStr = inRec[key];
        contact.bday = new Date(pomStr.slice(0,4), pomStr.slice(4,6),
            pomStr.slice(6,8));
      }
      else if (key === "c") {
        curRec = findSubElement("adr", "work");
        curRec.countryName = inRec[key];
      }
      else if (key === "cn") {
        contact.name = [inRec[key]];
      }
      else if (key === "description") {
        contact.note = [inRec[key]];
      }
      else if (key === "facsimiletelephonenumber") {
        curRec = findSubElement("tel", "fax");
        curRec.value = inRec[key];
      }
      else if (key === "givenName") {
        contact.givenName = [inRec[key]];
      }
      else if (key === "homePhone") {
        curRec = findSubElement("tel", "home");
        curRec.value = inRec[key];
      }
      else if (key === "l") {
        curRec = findSubElement("adr", "work");
        curRec.locality = inRec[key];
      }
      else if (key === "mail") {
        curRec = findSubElement("email", "work");
        curRec.value = inRec[key];
      }
      else if (key === "mobile") {
        curRec = findSubElement("tel", "mobile");
        curRec.value = inRec[key];
      }
      else if (key === "mozillaHomeCountryName") {
        curRec = findSubElement("adr", "home");
        curRec.countryName = inRec[key];
      }
      else if (key === "mozillaHomeLocalityName") {
        curRec = findSubElement("adr", "home");
        curRec.locality = inRec[key];
      }
      else if (key === "mozillaHomePostalCode") {
        curRec = findSubElement("adr", "home");
        curRec.postalCode = inRec[key];
      }
      else if (key === "mozillaHomeState") {
        curRec = findSubElement("adr", "home");
        curRec.region = inRec[key];
      }
      else if (key === "mozillaHomeStreet") {
        curRec = findSubElement("adr", "home");
        curRec.streetAddress = inRec[key];
      }
      else if (key === "mozillaHomeUrl") {
        curRec = findSubElement("url", "home");
        curRec.value = inRec[key];
      }
      else if (key === "mozillaNickname") {
        contact.nickname = [inRec[key]];
      }
      else if (key === "o") {
        contact.org = [inRec[key]];
      }
      else if (key === "sn") {
        contact.familyName = [inRec[key]];
      }
      else if (key === "st") {
        curRec = findSubElement("adr", "work");
        curRec.region = inRec[key];
      }
      else if (key === "street") {
        curRec = findSubElement("adr", "work");
        curRec.streetAddress = inRec[key];
      }
      else if (key === "telephoneNumber") {
        curRec = findSubElement("tel", "work");
        curRec.value = inRec[key];
      }
      else if (key === "title") {
        contact.jobTitle = [inRec[key]];
      }
      // Unknown attribute
      else {
        throw new ValueError("Unknown attribute " + key +
            " with value:\n" + inRec[key]);
      }
    }

    // Per RFC 4519 section 3.12 cn and sn attributes are always
    // required
    if (! contact.hasOwnProperty('name')) {
      if (contact.hasOwnProperty('familyName') &&
          contact.hasOwnProperty('givenName')) {
            contact.name = contact.givenName + " " + contact.familyName;
      }
      else if (contact.hasOwnProperty("org")) {
        contact.name = contact.org;
      }
      else if (contact.hasOwnProperty("jobTitle")) {
        contact.name = contact.jobTitle;
      }
    }

    if (Object.keys(contact).length > 0) {
      var outObj = new mozContact();
      outObj.init(contact);
      return contact;
    }
    return null;
  }


  /**
   * Not used presently and not debugged.
   */
  function whenContactAlreadyPresent(rec, cb_not_found, cb_found) {
    var rec_keys = Object.keys(rec);

    // Finding whether the contact has email or telephone
    var email_fields = rec_keys.intersection(emailFields);
    var phone_fields = rec_keys.intersection(phoneFields);
    var search_keys = [];

    if (email_fields.length > 0) {
      search_keys = email_fields;
    }
    else if (phone_fields.length > 0) {
      search_keys = phone_fields;
    }

    if (search_keys.length > 0) {
      var search_opts = {
        filterValue : rec(search_keys[0]),
        filterBy    : search_keys,
        filterOp    : "contains",
        filterLimit : 1
      };

      var search = navigator.mozContacts.find(search_opts);

      // Possible duplicate found ... bail out, rather do nothing!
      search.onsuccess = function() {
        // search.result is found record (Array of length 1)
        cb_found(rec, search.result);
      };

      // No duplicates, go ahead and create new record
      search.onerror = function() {
        cb_not_found(rec);
      };

    }
  }

  function restoreURLForm(url) {
    document.getElementById("progress-div").style.display = "none";
    document.getElementById("URL-form").style.display = "block";
    if (url !== undefined) {
      document.getElementsByName("URL")[0].value = url;
    }
  }

  function insertData(ldifText) {
    var progressEl = document.querySelector("#progress-div progress");

    var records = parseLDIF(ldifText.split("\n"));

    console.log("records.length = " + records.length);
    if (records.length > 0) {
      if (!window.confirm("THIS WILL ERASE ALL CONTACTS ON YOUR PHONE!\n" +
            "Are you cool with that?")) {
        return false;
      }
    }

    var cl_req = navigator.mozContacts.clear();

    cl_req.onerror = function() {
      throw new Error("Cannot clear whole Contacts database?");
    };

    cl_req.onsuccess = function() {
      progressEl.max = records.length;
      progressEl.value = 0;

      records.forEach(function (rec) {
        var add_contact = translateObjectToContact(rec);

        var sav_req = navigator.mozContacts.save(add_contact);

        sav_req.onsuccess = function() {
          progressEl.value += 1;
          if (progressEl.value >= records.length) {
            window.alert("All contacts have been imported!");
            restoreURLForm();
          }
        };

        sav_req.onerror = function() {
          console.error("Cannot save record " + add_contact.id +
            "\n" + rec.toSource());
        };
      });
    };

  }

  function submitHandler (evt) {
    var URL = document.getElementsByName("URL")[0].value;
    var progressForm = document.getElementById("progress-div");

    document.getElementById("URL-form").style.display = "none";
    progressForm.style.display = "block";

    var req = new XMLHttpRequest();
    req.open("GET", URL, true);
    var progressEl = progressForm.getElementsByTagName("progress")[0];

    req.onprogress = function(evt) {
      if (evt.lengthComputable) {
        progressEl.max = evt.total;
        progressEl.value = evt.loaded;
      }
    };

    req.onload = function() {
      var inText = req.responseText;
      if (inText.length > 0) {
        insertData(inText);

        if (localStorage) {
          localStorage.setItem("lastURL", URL);
        }

      }
    };

    req.onerror = function() {
      window.alert("Cannot load " + URL + "!");
      restoreURLForm(URL);
    };

    req.send();

    evt.stopPropagation();
    evt.preventDefault();
  }

  window.onload = function() {
    if (localStorage && localStorage.lastURL) {
      var oldURL = localStorage.getItem('lastURL');
      document.getElementsByName("URL")[0].value = oldURL;
    }

    document.body.addEventListener("submit",
        submitHandler, false);
  };

}());