PageRenderTime 60ms CodeModel.GetById 29ms RepoModel.GetById 1ms app.codeStats 0ms

/Apps/MergedContacts/static/js/client.js

https://github.com/quartzjer/Locker
JavaScript | 553 lines | 409 code | 53 blank | 91 comment | 139 complexity | 67caab2d5ef253b8685a7c25f18228db MD5 | raw file
  1. var log = function(msg) { if (console && console.log) console.debug(msg); };
  2. var displayedContact = '';
  3. var baseUrl = '';
  4. var hack = false;
  5. var total = 0;
  6. $(function() {
  7. $('a', $('.detail')).live('click', function() {
  8. top.location.href = $(this).attr('href');
  9. });
  10. $(document).keydown(function(e) {
  11. // disable enter
  12. if (e.keyCode === 13) return false;
  13. // esc key
  14. if (e.keyCode === 27) {
  15. return hack.loadAll();
  16. }
  17. if ($('.clicked').length != 0) {
  18. // TODO this logic is lame, need to port over my ListView class that does this correctly (justin)
  19. // down arrow
  20. if (e.keyCode === 40) {
  21. if ($('.clicked').next().length != 0) {
  22. $('.clicked').next().click();
  23. $("#main").scrollBy(0, $("#contacts li:first-of-type").outerHeight());
  24. return false;
  25. }
  26. // up arrow
  27. } else if (e.keyCode === 38) {
  28. if ($('.clicked').prev().length != 0) {
  29. $('.clicked').prev().click();
  30. $("#main").scrollBy(0, -$("#contacts li:first-of-type").outerHeight());
  31. return false;
  32. }
  33. }
  34. }
  35. });
  36. // Contact Model
  37. var Contact = Backbone.Model.extend({
  38. defaults: {}
  39. });
  40. // Contact Collection
  41. var AddressBook = Backbone.Collection.extend({
  42. model: Contact
  43. });
  44. // View used for details, overview
  45. var SideView = Backbone.View.extend({
  46. el: $('aside div.detail'),
  47. events: {},
  48. initialize: function() {
  49. _.bindAll(this, 'render'); // fixes loss of context for 'this' within methods
  50. that = this;
  51. this.render();
  52. },
  53. render: function() {
  54. // this.el.html("Hello!");
  55. }
  56. });
  57. // List View for Contacts
  58. var ListView = Backbone.View.extend({
  59. el: $('body'), // attaches `this.el` to an existing element.
  60. events: {
  61. 'click #showAllLink' : 'loadAll',
  62. 'hover #contacts li' : 'hoverContactHandler',
  63. 'click #contacts li' : 'clickContactHandler',
  64. },
  65. hoverContactHandler: function() {
  66. },
  67. clickContactHandler: function(ev) {
  68. var cid = $(ev.currentTarget).data('cid');
  69. if (cid === displayedContact) {
  70. return this.hideDetailsPane();
  71. }
  72. $('.clicked').removeClass('clicked');
  73. $(ev.currentTarget).addClass('clicked');
  74. this.drawDetailsPane(cid);
  75. },
  76. drawDetailsPane: function(cid) {
  77. displayedContact = cid;
  78. var self = this;
  79. var model = this.collection.get(cid);
  80. if(!(model.get('detailedData'))) {
  81. $.getJSON(baseUrl + '/Me/contacts/id/' + cid, function(contact) {
  82. model.set({detailedData : contact});
  83. self.updateDetails(contact);
  84. });
  85. } else {
  86. self.updateDetails(model.get('detailedData'));
  87. }
  88. },
  89. hideDetailsPane: function() {
  90. displayedContact = '';
  91. // $('aside').css('z-index', -1);
  92. // $('#main').stop().animate({
  93. // marginRight: '0px'}, 750, function() {
  94. // $('.detail').hide();
  95. // })
  96. return $('.clicked').removeClass('clicked');
  97. },
  98. addContact: function(contact) {
  99. var newContact = new Contact();
  100. if (contact.name) {
  101. var names = contact.name.split(' ');
  102. newContact.set({
  103. firstname: names[0],
  104. lastname: names[names.length - 1],
  105. name: contact.name
  106. })
  107. }
  108. newContact.set({
  109. name: contact.name,
  110. id: contact._id,
  111. });
  112. // copy email
  113. if (contact.emails) newContact.set({email: contact.emails[0].value});
  114. // copy photos
  115. if (contact.photos) newContact.set({photos: contact.photos});
  116. // copy accounts (twitter, github, facebook, foursquare)
  117. if(contact.accounts) {
  118. if(contact.accounts.twitter)
  119. newContact.set({twitterHandle: contact.accounts.twitter[0]});
  120. if(contact.accounts.github)
  121. newContact.set({github: contact.accounts.github[0]});
  122. if(contact.accounts.facebook)
  123. newContact.set({facebook: contact.accounts.facebook[0].data.link});
  124. if(contact.accounts.googleContacts) {
  125. // nothing for now, no unique data
  126. }
  127. if(contact.accounts.foursquare) {
  128. newContact.set({foursquare: contact.accounts.foursquare[0]});
  129. }
  130. if(contact.accounts.instagram) {
  131. newContact.set({instagram: contact.accounts.instagram[0]});
  132. }
  133. if(contact.accounts.tumblr) {
  134. newContact.set({tumblr: contact.accounts.tumblr[0]});
  135. }
  136. if(contact.accounts.flickr) {
  137. newContact.set({flickr: contact.accounts.flickr[0]});
  138. }
  139. }
  140. this.collection.add(newContact); // add item to collection; view is updated via event 'add'
  141. },
  142. initialize: function(){
  143. _.bindAll(this, 'load', 'render', 'addContact', 'loadSearch', 'loadSince', 'loadView'); // fixes loss of context for 'this' within methods
  144. that = this;
  145. that.collection = new AddressBook();
  146. console.log(window.location);
  147. if (window.location.hash.substr(0,4) == "#new") {
  148. that.loadSince(window.location.hash.substr(5));
  149. } else if (window.location.hash.substr(0,5) == "#view") {
  150. that.loadView(window.location.hash.substr(6));
  151. } else if (window.location.hash.substr(0,7) == "#search") {
  152. that.loadSearch(window.location.hash.substr(8))
  153. } else {
  154. that.loadAll();
  155. }
  156. $.getJSON(baseUrl + '/Me/contacts/state', {}, function(state) {
  157. total = state.count;
  158. $("#count").html(total);
  159. });
  160. },
  161. loadAll: function loadAll() {
  162. $("#appHeader h1").text("People");
  163. $("#appHeader .showAll").hide();
  164. this.hideDetailsPane();
  165. this.offset = 0;
  166. if(!hack) hack = this;
  167. $("#main").scroll(function(){
  168. if (($("#main").offset().top + $("#main").scrollTop()) >= ($("#main ul").height() - $("#main").height() - 250)){
  169. hack.load(function(){});
  170. }
  171. });
  172. this.collection._reset();
  173. this.load(function() {});
  174. },
  175. loadSince: function loadSince(objId) {
  176. var self = this;
  177. if(!hack) hack = this;
  178. $.getJSON(baseUrl + "/Me/contacts/since", {id:objId}, function(contacts) {
  179. $("#appHeader h1").text(contacts.length + " New " + (contacts.length == 1 ? "Person" : "People"));
  180. $("#appHeader .showAll").show();
  181. // $("#newHeader").show();
  182. for(var i in contacts) {
  183. self.addContact(contacts[i]);
  184. }
  185. self.render();
  186. })
  187. },
  188. loadView: function loadView(objId) {
  189. var self = this;
  190. if(!hack) hack = this;
  191. $.getJSON(baseUrl + "/Me/contacts/id/"+objId, function(contact) {
  192. // $("#newCount").text("Showing 1 Person");
  193. $("#appHeader h1").text("Showing 1 Person");
  194. $("#appHeader .showAll").show();
  195. // $("#newHeader").show();
  196. self.addContact(contact);
  197. self.render();
  198. })
  199. },
  200. /**
  201. * Load the contacts data (get contacts)
  202. * @param callback
  203. */
  204. load: function load(callback) {
  205. var that = this;
  206. //log("loading "+that.offset);
  207. if(!callback) callback = function(){};
  208. if(that.offset > total) return callback();
  209. if(that.loading) return callback();
  210. that.loading = true;
  211. var baseURL = baseUrl + '/query/getContact';
  212. var fields = "['_id','addresses','emails','name','phoneNumbers','photos','accounts.facebook.data.link'," +
  213. "'accounts.foursquare.data.id','accounts.github.data.login','accounts.instagram.data.username','accounts.tumblr.data.url','accounts.tumblr.data.title','accounts.twitter.data.screen_name'," +
  214. "'accounts.flickr.data.username','accounts.flickr.data.nsid']";
  215. var sort = '\'{"firstnamesort":1}\'';
  216. var terms = "[firstnamesort:\"a\"+]";
  217. $.getJSON(baseURL, {offset:that.offset, limit: 50, fields: fields, sort: sort, terms: terms}, function(contacts) {
  218. that.loading = false;
  219. for(var i in contacts) {
  220. that.addContact(contacts[i]);
  221. }
  222. that.render();
  223. that.offset += 50;
  224. return callback();
  225. });
  226. },
  227. /**
  228. * Load the contacts data from a search result
  229. * @param callback
  230. */
  231. loadSearch: function loadSearch(q) {
  232. var that = this;
  233. if(!hack) hack = this;
  234. // $("#newCount").text("Showing Search Results");
  235. $("#appHeader h1").text("Showing Search Results");
  236. $("#appHeader .showAll").show();
  237. // $("#newHeader").show();
  238. log("searching "+q);
  239. that.collection._reset();
  240. var baseURL = baseUrl + '/Me/search/query';
  241. var type = 'contact*';
  242. $.getJSON(baseURL, {q: q + "*", type: type, limit: 20}, function(results) {
  243. for(var i in results.hits) {
  244. that.addContact(results.hits[i].fullobject);
  245. }
  246. that.render({q:q}); // additionally filter
  247. return;
  248. });
  249. },
  250. /**
  251. * Update the details panel with a given contact
  252. * @param the object containing all of the information about the contact
  253. */
  254. updateDetails: function(contact) {
  255. $('.name').text(contact.name);
  256. $('.photo').attr('src', this.getPhoto(contact, true));
  257. $('.contact-details').show();
  258. $('.social-details').show();
  259. // twitter
  260. if (contact.accounts.twitter && contact.accounts.twitter[0].data) {
  261. var twitter = contact.accounts.twitter[0].data;
  262. $('.twitterhandle').attr('href', 'http://www.twitter.com/' + twitter.screen_name);
  263. $('.twitterhandle').text('@' + twitter.screen_name);
  264. // $('.lasttweet').text(twitter.status.text);
  265. $('.twitterSection').show();
  266. } else {
  267. $('.twitterSection').hide();
  268. }
  269. // email
  270. $('.emailaddress').text(this.getEmail(contact));
  271. $('.emailaddress').attr('href', 'mailto:' + this.getEmail(contact));
  272. // phone
  273. var phone = this.getPhone(contact);
  274. $('.phonenumber').text(phone);
  275. if (phone) {
  276. $('.phoneSection').show();
  277. } else {
  278. $('.phoneSection').hide();
  279. }
  280. // 4sq
  281. if (contact.accounts.foursquare && contact.accounts.foursquare[0].data) {
  282. var fsq = contact.accounts.foursquare[0].data;
  283. $('.4sqlastseen').attr('href', 'http://www.foursquare.com/user/' + fsq.id + '/checkin/' + fsq.checkins.items[0].id);
  284. $('.4sqlastseen').text(fsq.checkins.items[0].venue.name);
  285. $('.foursquarehandle').attr('href', 'http://www.foursquare.com/user/' + fsq.id);
  286. $('.foursquareSection').show();
  287. } else {
  288. $('.foursquareSection').hide();
  289. }
  290. // gh
  291. if (contact.accounts.github && contact.accounts.github[0].data) {
  292. var gh = contact.accounts.github[0].data;
  293. $('.githubHandle').text(gh.login);
  294. $('.githubHandle').attr('href', 'http://www.github.com/' + gh.login);
  295. $('.githubSection').show();
  296. } else {
  297. $('.githubSection').hide();
  298. }
  299. // fb
  300. if (contact.accounts.facebook && contact.accounts.facebook[0].data) {
  301. console.log("blah")
  302. var fb = contact.accounts.facebook[0].data;
  303. $('.facebookHandle').attr('href', fb.link);
  304. $('.facebookHandle').text(fb.name);
  305. $('.facebookSection').show();
  306. } else {
  307. $('.facebookSection').hide();
  308. }
  309. // flickr
  310. if (contact.accounts.flickr && contact.accounts.flickr[0].data) {
  311. var flickr = contact.accounts.flickr[0].data;
  312. $('.flickrHandle').attr('href', flickr.link);
  313. $('.flickrHandle').text(flickr.username);
  314. $('.flickrSection').show();
  315. } else {
  316. $('.flickrSection').hide();
  317. }
  318. // tumblr
  319. if (contact.accounts.tumblr && contact.accounts.tumblr[0].data) {
  320. var tumblr = contact.accounts.tumblr[0].data;
  321. $('.tumblrHandle').attr('href', tumblr.url);
  322. $('.tumblrHandle').text(tumblr.title);
  323. $('.tumblrSection').show();
  324. } else {
  325. $('.tumblrSection').hide();
  326. }
  327. // location
  328. var loc = this.getLocation(contact);
  329. $('.address').text(loc);
  330. if (loc) {
  331. $('.addressSection').show();
  332. $('.address').attr('href', 'http://maps.google.com/maps?q=' + encodeURI(loc));
  333. } else {
  334. $('.addressSection').hide();
  335. }
  336. // animation
  337. if (!$('.detail').is(':visible')) {
  338. $('.empty').fadeOut();
  339. $('.detail').fadeIn();
  340. // $('#main').stop().animate({
  341. // marginRight: '374px'}, 750, function() {
  342. // $('aside').css('z-index', 1);
  343. // });
  344. }
  345. // Hide empty contact / social details sections, if necessary
  346. $('.contact-details li:visible').length == 0 ? $('.contact-details').hide() : $('.contact-details').show();
  347. $('.social-details li:visible').length == 0 ? $('.social-details').hide() : $('.social-details').show();
  348. console.log($('.social-details li:visible'))
  349. },
  350. /**
  351. * Add the person's twitter username to a div
  352. * @param div - $(HTMLElement)
  353. * @param contact - contact obj
  354. */
  355. getTwitter: function(contact) {
  356. var twitterUsername;
  357. if(contact.accounts.twitter && contact.accounts.twitter[0].data
  358. && contact.accounts.twitter[0].data.screen_name) {
  359. twitterUsername = contact.accounts.twitter[0].data.screen_name;
  360. }
  361. if(twitterUsername) {
  362. return twitterUsername;
  363. }
  364. return false;
  365. },
  366. /**
  367. * Add the person's Facebook details to a div
  368. * @param contact {Object} Contact Object
  369. */
  370. getFacebook: function(contact) {
  371. var facebookUsername;
  372. return false;
  373. },
  374. /**
  375. * get the location of a contact
  376. * @param contact - contact obj
  377. */
  378. getLocation: function(contact) {
  379. if(contact.addresses && contact.addresses.length) {
  380. for(var i in contact.addresses) {
  381. if(contact.addresses[i].type === 'location') {
  382. return contact.addresses[i].value;
  383. }
  384. }
  385. }
  386. return '';
  387. },
  388. /**
  389. * get the phone number of a contact
  390. * @param contact - contact obj
  391. */
  392. getPhone: function(contact) {
  393. if (contact.phoneNumbers && contact.phoneNumbers.length) {
  394. return contact.phoneNumbers[0].value;
  395. }
  396. return '';
  397. },
  398. /**
  399. * Get the person's email address
  400. * @param contact - contact obj
  401. */
  402. getEmail: function(contact) {
  403. if (contact.emails && contact.emails.length) {
  404. return contact.emails[0].value;
  405. }
  406. return '';
  407. },
  408. /**
  409. * Get the person's GitHub details
  410. * @param contact {Object} contact obj
  411. */
  412. getGithub: function(contact) {
  413. },
  414. /**
  415. * Convience function to get the url from an object
  416. * @param contact {Object} Contact object (json)
  417. * @param fullsize {Boolean} Optional, default false. Use a large photo instead of small
  418. */
  419. getPhoto: function(contact, fullsize) {
  420. var url = 'img/silhouette.png';
  421. if(contact.photos && contact.photos.length) {
  422. url = contact.photos[0];
  423. //twitter
  424. if(fullsize && url.match(/_normal\.(jpg||png)/) && !url.match(/.*default_profile_([0-9])_normal\.png/)) {
  425. url = url.replace(/_normal\.(jpg||png)/, '.$1');
  426. }
  427. else if(url.indexOf('https://graph.facebook.com/') === 0) {
  428. if(fullsize)
  429. url += "?return_ssl_resources=true&type=large";
  430. else
  431. url += "?return_ssl_resources=true&type=square";
  432. }
  433. }
  434. return url;
  435. },
  436. render: function(config){
  437. // default to empty
  438. config = config || {};
  439. log("rendering "+config.q);
  440. var contactsEl, contactTemplate, contactsHTML, addContactToHTML;
  441. var that = this;
  442. contactsEl = $("#contacts");
  443. contactsEl.html('');
  444. contactsHTML = "";
  445. // I could put this in a script tag on the page,
  446. // but i kind of like being able to comment lines
  447. contactTemplate = '<li class="contact" data-cid="<%= id %>">';
  448. contactTemplate += ' <div class="contact-avatar"><img src="<% if (typeof(smPhoto) != "undefined" ) { %><%= smPhoto %><% } else { %>/static/img/lock.png<% } %>"/></div>';
  449. contactTemplate += ' <div class="contact-name"><% if (typeof(name) != "undefined") { %><%= name %><% } %></div>';
  450. contactTemplate += ' <div class="contact-actions">';
  451. contactTemplate += ' <% if (typeof(email) != "undefined") { %><a href="mailto:<%= email %>" target="_blank" class="social_link email">Email</a><% } %> ';
  452. contactTemplate += ' <% if (typeof(facebook) != "undefined") { %><a href="<%= facebook %>" class="social_link facebook" target="_blank">Facebook Profile</a><% } %>';
  453. contactTemplate += ' <% if (typeof(twitterHandle) != "undefined" && typeof(twitterHandle.data.screen_name) != "undefined") { %><a href="http://twitter.com/<%= twitterHandle.data.screen_name %>" class="social_link twitter" target="_blank">Twitter Profile</a><% } %>';
  454. contactTemplate += ' <% if (typeof(flickr) != "undefined" && typeof(flickr.data.username) != "undefined") { %><a href="http://flickr.com/people/<%= flickr.data.nsid %>" class="social_link flickr" target="_blank">Flickr Profile</a><% } %>';
  455. contactTemplate += ' <% if (typeof(foursquare) != "undefined" && typeof(foursquare.data.id) != "undefined") { %><a href="http://foursquare.com/user/<%= foursquare.data.id %>" class="social_link foursquare" target="_blank">Foursquare Profile</a><% } %>';
  456. contactTemplate += ' <% if (typeof(github) != "undefined" && typeof(github.data.login) != "undefined") { %><a href="http://github.com/<%= github.data.login %>" class="social_link github" target="_blank">GitHub Profile</a><% } %>';
  457. contactTemplate += ' <% if (typeof(instagram) != "undefined" && typeof(instagram.data.username) != "undefined") { %><a href="http://listagr.am/n/<%= instagram.data.username %>" class="social_link instagram" target="_blank">Instagram Pics</a><% } %>';
  458. contactTemplate += ' <% if (typeof(tumblr) != "undefined" && typeof(tumblr.data.url) != "undefined") { %><a href="<%= tumblr.data.url %>" class="social_link tumblr" target="_blank"><%= tumblr.data.url %></a><% } %>';
  459. contactTemplate += ' </div>';
  460. contactTemplate += '</li>';
  461. addContactToHTML = function(c) {
  462. // create a simple json obj to use for creating the template (if necessary)
  463. if (typeof(c.get('html')) == "undefined") {
  464. var tmpJSON = c.toJSON();
  465. if (typeof(tmpJSON.name) == 'undefined' && typeof(tmpJSON.email) == 'undefined') {
  466. return;
  467. }
  468. if (typeof(tmpJSON.name) == "undefined") {
  469. tmpJSON.name = tmpJSON.email;
  470. }
  471. tmpJSON.smPhoto = that.getPhoto(tmpJSON, false);
  472. tmpJSON.photo = that.getPhoto(tmpJSON, true);
  473. tmpJSON.json = JSON.stringify(tmpJSON, null, 2);
  474. // cache compiled template to the model
  475. var compiledTemplate = _.template(contactTemplate, tmpJSON);
  476. c.set({'html': compiledTemplate});
  477. contactsEl.append(compiledTemplate);
  478. // contactsHTML += compiledTemplate;
  479. } else {
  480. // just get the rendered html from our model
  481. contactsEl.append(c.get('html'));
  482. }
  483. };
  484. _.each(that.collection.filter(function(){return true;}), addContactToHTML);
  485. if ($('.contact').length === 1) {
  486. this.drawDetailsPane($('.contact').data('cid'));
  487. $('.contact').addClass('clicked');
  488. } else if ($('.detail').is(':visible')) {
  489. var selectedContact = $(".contact[data-cid='" + displayedContact + "']");
  490. if (selectedContact.length > 0) {
  491. selectedContact.addClass('clicked');
  492. } else {
  493. this.hideDetailsPane();
  494. }
  495. }
  496. }
  497. });
  498. var listView = new ListView();
  499. var sideView = new SideView();
  500. });