addressbook.js 66 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714
  1. /*
  2. CardDavMATE - the open source CardDAV Web Client
  3. Copyright (C) 2011-2015
  4. Jan Mate <jan.mate@inf-it.com>
  5. Andrej Lezo <andrej.lezo@inf-it.com>
  6. Matej Mihalik <matej.mihalik@inf-it.com>
  7. This program is free software: you can redistribute it and/or modify
  8. it under the terms of the GNU Affero General Public License as
  9. published by the Free Software Foundation, either version 3 of the
  10. License, or (at your option) any later version.
  11. This program is distributed in the hope that it will be useful,
  12. but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. GNU Affero General Public License for more details.
  15. You should have received a copy of the GNU Affero General Public License
  16. along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. // AddressbookList Class
  19. function AddressbookList()
  20. {
  21. this.contacts=new Array();
  22. this.contacts_hash=new Object();
  23. this.contacts_hash_uidattr=new Object();
  24. this.companies=new Array();
  25. this.companies_hash=new Object();
  26. this.companies_hash_uidattr=new Object();
  27. this.vcard_groups=new Object();
  28. this.contact_categories=new Object();
  29. this.contact_companies=new Object();
  30. this.contactLoaded=null;
  31. this.contactToReload=null;
  32. this.vcardGroupLoaded=null;
  33. this.reset=function()
  34. {
  35. this.contacts.splice(0,this.contacts.length);
  36. this.contacts_hash=new Object();
  37. this.contacts_hash_uidattr=new Object();
  38. this.companies.splice(0,this.companies.length);
  39. this.companies_hash=new Object();
  40. this.companies_hash_uidattr=new Object();
  41. this.vcard_groups=new Object(); // these are not removed from the interface (it's OK)
  42. this.contact_categories=new Object();
  43. this.contact_companies=new Object();
  44. this.contactLoaded=null;
  45. this.contactToReload=null;
  46. this.vcardGroupLoaded=null;
  47. };
  48. this.getNewUID=function()
  49. {
  50. // we count with uniqueness of generated hash string
  51. var newUID=null;
  52. newUID=generateUID();
  53. return newUID;
  54. };
  55. this.getLoadedContactUID=function()
  56. {
  57. if(this.contactLoaded!=null)
  58. return this.contactLoaded.uid;
  59. else
  60. return '';
  61. };
  62. this.getSortKey=function(inputContact, inputSettings, inputMode) // inputMode (0=sort, 1=display)
  63. {
  64. var vcard_element=('\r\n'+inputContact.vcard).match(vCard.pre['contentline_N']);
  65. if(vcard_element===null || vcard_element.length!==1) // if the N attribute is not present exactly once, vCard is considered invalid
  66. return false;
  67. var sortKeyCompanyPart='';
  68. if(typeof (getCRMSortKey)== 'function' && inputMode==0)
  69. {
  70. sortKeyCompanyPart=getCRMSortKey(inputContact);
  71. if(inputContact.isCompany!=undefined && inputContact.isCompany)
  72. return sortKeyCompanyPart; // for company contact we can return here
  73. }
  74. else if(typeof globalGroupContactsByCompanies!='undefined' && globalGroupContactsByCompanies==true)
  75. {
  76. var sortKeyCompanyPart='\u0009';
  77. var vcard_orgname=('\r\n'+inputContact.vcard).match(vCard.pre['contentline_ORG']);
  78. if(vcard_orgname!=null && vcard_orgname.length>0) // if more than one ORG is present, use the first one
  79. {
  80. // parsed (contentline_parse) = [1]->"group.", [2]->"name", [3]->";param;param", [4]->"value"
  81. var parsed=vcard_orgname[0].match(vCard.pre['contentline_parse']);
  82. var parsed_value=vcardSplitValue(parsed[4], ';');
  83. sortKeyCompanyPart=parsed_value[0]+'\u0009'+(parsed_value[1]!=undefined ? parsed_value[1] : '')+'\u0009';
  84. if(inputMode==0 && inputContact.isCompany!=undefined && inputContact.isCompany)
  85. return sortKeyCompanyPart; // for company contact we can return here
  86. }
  87. }
  88. var tmp = [];
  89. var isGroup = this.isContactGroup(inputContact.vcard);
  90. /* backward compatibility for stupid users (remove it in future) */
  91. if(typeof inputSettings==='string')
  92. tmp = inputSettings.replace(RegExp(',','g'), ', ').split(',');
  93. else if($.isArray(inputSettings)) /* new configuration options (arrays) */
  94. tmp = inputSettings.slice(); // copy the configuration array
  95. // display settings for non-group contacts need some flattening
  96. if(inputMode===1 && !isGroup) {
  97. tmp = $.map(tmp, function(el) {
  98. if($.isPlainObject(el.value)) {
  99. return el.value;
  100. }
  101. else {
  102. return [el.value];
  103. }
  104. });
  105. }
  106. // now flatten the array completely to a company / personal version
  107. tmp = $.map(tmp, function(el) {
  108. if($.isPlainObject(el)) {
  109. if(inputContact.isCompany && el.hasOwnProperty('company')) {
  110. return [el.company];
  111. }
  112. else if(!inputContact.isCompany && el.hasOwnProperty('personal')) {
  113. return [el.personal];
  114. }
  115. return [];
  116. }
  117. return [el];
  118. });
  119. for(var i=0; i<tmp.length; i++) {
  120. tmp[i] = getContactDataColumn(inputContact, tmp[i]);
  121. }
  122. sort_value = tmp.join(' ').trim();
  123. if(sort_value==='' && isGroup) // if we didn't get a proper sort value for group contacts, use FN
  124. {
  125. var vcard_element2=('\r\n'+inputContact.vcard).match(vCard.pre['contentline_FN']);
  126. if(vcard_element2!=null && vcard_element2.length==1) // if the FN attribute is not present exactly once, vCard is considered invalid
  127. {
  128. // parsed (contentline_parse) = [1]->"group.", [2]->"name", [3]->";param;param", [4]->"value"
  129. var parsed=vcard_element2[0].match(vCard.pre['contentline_parse']);
  130. var sort_value=parsed[4];
  131. }
  132. }
  133. return (inputMode===0 ? sortKeyCompanyPart+sort_value : sort_value);
  134. };
  135. this.isContactGroup=function(inputVcard)
  136. {
  137. var vcard_element=null;
  138. if((vcard_element=('\r\n'+inputVcard).match(vCard.pre['X-ADDRESSBOOKSERVER-KIND']))!=null)
  139. {
  140. // parsed (contentline_parse) = [1]->"group.", [2]->"name", [3]->";param;param", [4]->"value"
  141. var parsed=vcard_element[0].match(vCard.pre['contentline_parse']);
  142. if(parsed[4].toLowerCase()=='group')
  143. return true;
  144. }
  145. return false;
  146. };
  147. this.getMyContactGroups=function(inputUid)
  148. {
  149. if(this.contacts_hash[inputUid]!=undefined)
  150. {
  151. var myContactGroups=new Array();
  152. if((vcard_element=this.contacts_hash[inputUid].vcard.match(vCard.pre['contentline_UID']))!=null)
  153. {
  154. // parsed (contentline_parse) = [1]->"group.", [2]->"name", [3]->";param;param", [4]->"value"
  155. parsed=vcard_element[0].match(vCard.pre['contentline_parse']);
  156. for(var j=0;j<this.vcard_groups[inputUid.replace(RegExp('/[^/]*$',''),'/')].length;j++)
  157. {
  158. if(this.vcard_groups[inputUid.replace(RegExp('/[^/]*$',''),'/')][j].vcard.match(RegExp('\r\nX-ADDRESSBOOKSERVER-MEMBER:urn:uuid:'+parsed[4]+'\r\n','mi')))
  159. myContactGroups[myContactGroups.length]=this.vcard_groups[inputUid.replace(RegExp('/[^/]*$',''),'/')][j].uid;
  160. }
  161. }
  162. return myContactGroups;
  163. }
  164. else
  165. return null;
  166. };
  167. this.getRemoveMeFromContactGroups=function(inputUid, inputContactGroupsUidArr)
  168. {
  169. if(this.contacts_hash[inputUid]!=undefined)
  170. {
  171. var changedContactGroups=new Array();
  172. if((vcard_element=this.contacts_hash[inputUid].vcard.match(vCard.pre['contentline_UID']))!=null)
  173. {
  174. // parsed (contentline_parse) = [1]->"group.", [2]->"name", [3]->";param;param", [4]->"value"
  175. parsed=vcard_element[0].match(vCard.pre['contentline_parse']);
  176. for(var j=0;j<this.vcard_groups[inputUid.replace(RegExp('/[^/]*$',''),'/')].length;j++)
  177. {
  178. if(inputContactGroupsUidArr!=null)
  179. {
  180. var skipThis=true;
  181. for(var k=0;k<inputContactGroupsUidArr.length;k++)
  182. if(inputContactGroupsUidArr[k]==this.vcard_groups[inputUid.replace(RegExp('/[^/]*$',''),'/')][j].uid)
  183. {
  184. skipThis=false;
  185. break;
  186. }
  187. if(skipThis==true)
  188. continue;
  189. }
  190. var vcard=this.vcard_groups[inputUid.replace(RegExp('/[^/]*$',''),'/')][j].vcard;
  191. var changedVcard=null;
  192. if(vcard!=(changedVcard=vcard.replaceAll('\r\nX-ADDRESSBOOKSERVER-MEMBER:urn:uuid:'+parsed[4]+'\r\n','\r\n')))
  193. {
  194. // update the revision in the group vcard
  195. var d = new Date();
  196. utc=d.getUTCFullYear()+(d.getUTCMonth()+1<10 ? '0':'')+(d.getUTCMonth()+1)+(d.getUTCDate()<10 ? '0':'')+d.getUTCDate()+'T'+(d.getUTCHours()<10 ? '0':'')+d.getUTCHours()+(d.getUTCMinutes()<10 ? '0':'')+d.getUTCMinutes()+(d.getUTCSeconds()<10 ? '0':'')+d.getUTCSeconds()+'Z';
  197. changedVcard=changedVcard.replace(RegExp('\r\nREV:.*\r\n','mi'),'\r\nREV:'+utc+'\r\n');
  198. // "copy" of the original object
  199. changedContactGroups[changedContactGroups.length]=$.extend({},this.vcard_groups[inputUid.replace(RegExp('/[^/]*$',''),'/')][j]);
  200. // new modified vcard group
  201. changedContactGroups[changedContactGroups.length-1].vcard=changedVcard;
  202. }
  203. }
  204. }
  205. return changedContactGroups;
  206. }
  207. else
  208. return null;
  209. };
  210. this.getAddMeToContactGroups=function(inputContactObj, inputContactGroupsUidArr)
  211. {
  212. if(!(inputContactGroupsUidArr instanceof Array))
  213. inputContactGroupsUidArr=[inputContactGroupsUidArr];
  214. vcard_element=inputContactObj.vcard.match(vCard.pre['contentline_UID']);
  215. // parsed (contentline_parse) = [1]->"group.", [2]->"name", [3]->";param;param", [4]->"value"
  216. parsed=vcard_element[0].match(vCard.pre['contentline_parse']);
  217. var changedContactGroups=new Array();
  218. for(var j=0;j<this.vcard_groups[inputContactObj.uid.replace(RegExp('/[^/]*$',''),'/')].length;j++)
  219. for(var k=0;k<inputContactGroupsUidArr.length;k++)
  220. if(this.vcard_groups[inputContactObj.uid.replace(RegExp('/[^/]*$',''),'/')][j].uid==inputContactGroupsUidArr[k])
  221. {
  222. // if the uuid is already a member we remove it from contact-group to avoid duplicate membership
  223. var vcard=this.vcard_groups[inputContactObj.uid.replace(RegExp('/[^/]*$',''),'/')][j].vcard.replaceAll('\r\nX-ADDRESSBOOKSERVER-MEMBER:urn:uuid:'+parsed[4]+'\r\n','\r\n');
  224. var tmp=vcard.split('\r\n');
  225. tmp.splice(tmp.length-2,0,'X-ADDRESSBOOKSERVER-MEMBER:urn:uuid:'+parsed[4]);
  226. var changedVcard=tmp.join('\r\n');
  227. var d = new Date();
  228. utc=d.getUTCFullYear()+(d.getUTCMonth()+1<10 ? '0':'')+(d.getUTCMonth()+1)+(d.getUTCDate()<10 ? '0':'')+d.getUTCDate()+'T'+(d.getUTCHours()<10 ? '0':'')+d.getUTCHours()+(d.getUTCMinutes()<10 ? '0':'')+d.getUTCMinutes()+(d.getUTCSeconds()<10 ? '0':'')+d.getUTCSeconds()+'Z';
  229. changedVcard=changedVcard.replace(RegExp('\r\nREV:.*\r\n','mi'),'\r\nREV:'+utc+'\r\n');
  230. // "copy" of the original object
  231. changedContactGroups[changedContactGroups.length]=$.extend({},this.vcard_groups[inputContactObj.uid.replace(RegExp('/[^/]*$',''),'/')][j]);
  232. // new modified vcard group (normalisation is added to fix basic errors in invalid vCard)
  233. changedContactGroups[changedContactGroups.length-1].vcard=normalizeVcard(changedVcard);
  234. }
  235. return changedContactGroups;
  236. };
  237. // Contact group list is not sorted, instead "insert sort" is performed
  238. this.insertContactGroup=function(inputContact, forceReload, forceReinsert)
  239. {
  240. if((inputContact.sortkey=this.getSortKey(inputContact, [['{LastName}']], 0))===false || (inputContact.displayvalue=this.getSortKey(inputContact, [['{LastName}']], 1))===false)
  241. return false; //invalid vcard
  242. var makeActive=null;
  243. var makeChecked=null;
  244. // do not insert entry with duplicate UID
  245. for(var i=0;i<this.vcard_groups[inputContact.uid.replace(RegExp('/[^/]*$',''),'/')].length;i++)
  246. if(this.vcard_groups[inputContact.uid.replace(RegExp('/[^/]*$',''),'/')][i].uid==inputContact.uid)
  247. {
  248. if(forceReinsert==false && this.vcard_groups[inputContact.uid.replace(RegExp('/[^/]*$',''),'/')][i].displayvalue==inputContact.displayvalue)
  249. {
  250. this.vcard_groups[inputContact.uid.replace(RegExp('/[^/]*$',''),'/')][i]=inputContact;
  251. return 0;
  252. }
  253. else
  254. {
  255. if($('#ResourceCardDAVList').find('[data-id='+jqueryEscapeSelector(inputContact.uid)+']').hasClass('resourceCardDAV_selected'))
  256. makeActive=inputContact.uid;
  257. if(dataGetChecked('#ResourceCardDAVList').indexOf(inputContact.uid)!=-1 || dataGetChecked('#ResourceCardDAVList').indexOf(inputContact.uid.replace(RegExp('/[^/]*$',''),'/'))!=-1)
  258. makeChecked=inputContact.uid;
  259. // the contact group name is changed and must be moved to correct place (we first remove it and then reinsert)
  260. this.removeContactGroup(inputContact.uid, false);
  261. break;
  262. }
  263. }
  264. // find the index where to insert the new contact group
  265. var insertIndex=this.vcard_groups[inputContact.uid.replace(RegExp('/[^/]*$',''),'/')].length;
  266. for(var i=0;i<this.vcard_groups[inputContact.uid.replace(RegExp('/[^/]*$',''),'/')].length;i++)
  267. if(this.vcard_groups[inputContact.uid.replace(RegExp('/[^/]*$',''),'/')][i].sortkey.customCompare(inputContact.sortkey,globalSortAlphabet,1,false)==1)
  268. {
  269. insertIndex=i;
  270. break;
  271. }
  272. // insert the contact group
  273. this.vcard_groups[inputContact.uid.replace(RegExp('/[^/]*$',''),'/')].splice(insertIndex, 0, inputContact);
  274. // insert the contact group to interface
  275. var newElement=globalTranslCardDAVListItem.find('.contact_group').find('.group').clone();
  276. // the onclick event is disabled until the last drag&drop operation is completed
  277. newElement.click(function(e){
  278. if(globalAddressbookCollectionsLoading)
  279. return true;
  280. if(e.shiftKey) {
  281. var uid = $(this).attr('data-id');
  282. $('#ResourceCardDAVList').find('.resourceCardDAV:visible').children('input[type="checkbox"]').each(function(){
  283. var currentUid = $(this).attr('data-id');
  284. $(this).prop({'checked':false, 'indeterminate':false}).attr('data-ind', 'true');
  285. collectionChBoxClick(this, '#ResourceCardDAVList', '.resourceCardDAV_header', '.resourceCardDAV', '.contact_group', false);
  286. });
  287. var checkbox = $(this).children('input[type="checkbox"]');
  288. checkbox.prop({'checked':true, 'indeterminate':false});
  289. groupChBoxClick(checkbox.get(0), '#ResourceCardDAVList', '.resourceCardDAV_header', '.resourceCardDAV', '.contact_group', false);
  290. globalAddressbookList.applyABFilter([uid], false);
  291. }
  292. globalResourceCardDAVList.resourceOrGroupClick(this.getAttribute('data-id'));
  293. });
  294. newElement.attr('data-id',inputContact.uid);
  295. newElement.find('.resourceCardDAVGroupColor').css('background-color', inputContact.color);
  296. // note: we need to check the group if the parent collection is checked (and we need to use .attr() instead of .prop() because the element is not in the DOM)
  297. var tmp_check=false;
  298. if($('#ResourceCardDAVList').find('[data-id='+jqueryEscapeSelector(inputContact.uid.replace(RegExp('[^/]*$',''),''))+']').find('input[type=checkbox]').prop('checked')==true && $('#ResourceCardDAVList').find('[data-id='+jqueryEscapeSelector(inputContact.uid.replace(RegExp('[^/]*$',''),''))+']').find('input[type=checkbox]').prop('indeterminate')==false)
  299. tmp_check=true;
  300. newElement.find('input[type=checkbox]').attr('checked', tmp_check).attr({'data-id': inputContact.uid, 'onclick': 'var evt=arguments[0]; evt.stopPropagation(); if($(this).parents(\':eq(2)\').find(\'[class^="r_"]\').length>0) return false; else globalAddressbookList.applyABFilter(groupChBoxClick(this, \'#ResourceCardDAVList\', \'.resourceCardDAV_header\', \'.resourceCardDAV\', \'.contact_group\', true), false);'});
  301. newElement.append(vcardUnescapeValue(inputContact.displayvalue));
  302. newElement.css('display','');
  303. if($('#ResourceCardDAVList').find('[data-id="'+jqueryEscapeSelector(inputContact.uid.replace(RegExp('/[^/]*$',''),'/'))+'"]').next('.contact_group').find('[data-id="'+jqueryEscapeSelector(inputContact.uid)+'"]').length==0)
  304. $('#ResourceCardDAVList').find('[data-id="'+jqueryEscapeSelector(inputContact.uid.replace(RegExp('/[^/]*$',''),'/'))+'"]').next('.contact_group').children().eq(insertIndex).after(newElement);
  305. // make the area droppable if the collection is not read-only
  306. if(globalResourceCardDAVList.getCollectionPrivByUID(inputContact.uid.replace(RegExp('[^/]*$',''),''))==false && (typeof globalDisableDragAndDrop=='undefined' || globalDisableDragAndDrop!=true))
  307. $('#ResourceCardDAVList').find('[data-id="'+jqueryEscapeSelector(inputContact.uid.replace(RegExp('[^/]*$',''),''))+'"]').parent().find('.contact_group').children().eq(insertIndex+1).droppable({
  308. accept: '.ablist_item',
  309. tolerance: 'pointer',
  310. hoverClass: 'group_dropped_to',
  311. drop: function(event, ui){
  312. // animate the clone of the dropped (draggable) element
  313. var tmp=ui.helper.clone();
  314. tmp.appendTo('body')
  315. .animate({opacity: 0, color: 'transparent', height: 0, width: 0, fontSize: 0, lineHeight: 0, paddingLeft: 0, paddingRight: 0},750,function(){tmp.remove()});
  316. // disallow to drag the original dropped element until the processing is finished
  317. ui.draggable.draggable('option', 'disabled', true);
  318. // animate the original dropped element
  319. ui.draggable.animate({opacity: 0.3}, 750);
  320. // disallow to drop any new element until the processing is finished
  321. $(this).droppable('option', 'disabled', true);
  322. // show the loader icon
  323. $(this).addClass('r_operate');
  324. var tmp2=globalAddressbookList.getContactByUID(ui.draggable.attr('data-id'));
  325. tmp2.addToContactGroupUID='';
  326. tmp2.removeToContactGroupUID=new Array();
  327. tmp2.addToContactGroupUID=$(this).attr('data-id');
  328. tmp2.uiObjects={contact: ui.draggable, resource: $(this).attr('data-id')};
  329. lockAndPerformToCollection(tmp2, globalRefAddContact.attr('data-filter-url'), 'ADD_TO_GROUP');
  330. }
  331. });
  332. // if no new makeActive but forceReload is true then reload the current contact group
  333. if(makeActive==null && forceReload==true)
  334. makeActive=globalRefAddContact.attr('data-filter-url');
  335. // load the contact group if it was selected
  336. if(makeActive!=null)
  337. {
  338. $('#ResourceCardDAVList').find('.resourceCardDAV_item').find('.resourceCardDAV_selected').removeClass('resourceCardDAV_selected');
  339. $('#ResourceCardDAVList').find('[data-id='+jqueryEscapeSelector(makeActive.replace(RegExp('[^/]*$',''),''))+']').addClass('resourceCardDAV_selected');
  340. $('#ResourceCardDAVList').find('[data-id='+jqueryEscapeSelector(makeActive)+']').addClass('resourceCardDAV_selected');
  341. }
  342. if(makeChecked!=null)
  343. {
  344. $('#ResourceCardDAVList').find('[data-id='+jqueryEscapeSelector(makeChecked)+']').find('input[type=checkbox]').prop('checked',true);
  345. this.applyABFilter(dataGetChecked('#ResourceCardDAVList'), false);
  346. }
  347. };
  348. this.removeContactGroup=function(inputUid, loadNext)
  349. {
  350. for(var i=this.vcard_groups[inputUid.replace(RegExp('/[^/]*$',''),'/')].length-1;i>=0;i--)
  351. if(this.vcard_groups[inputUid.replace(RegExp('/[^/]*$',''),'/')][i].uid==inputUid)
  352. {
  353. var uidRemoved=this.vcard_groups[inputUid.replace(RegExp('/[^/]*$',''),'/')][i].uid;
  354. var item=$('#ResourceCardDAVList').find('[data-id^="'+jqueryEscapeSelector(this.vcard_groups[inputUid.replace(RegExp('/[^/]*$',''),'/')][i].uid)+'"]');
  355. // remove the item
  356. item.remove();
  357. this.vcard_groups[inputUid.replace(RegExp('/[^/]*$',''),'/')].splice(i,1);
  358. // vcardGroupLoaded bolo zrusene, pozriet co s tym
  359. if(loadNext && this.vcardGroupLoaded!=null && this.vcardGroupLoaded.uid==inputUid)
  360. {
  361. this.vcardGroupLoaded=null;
  362. // set the whole collection as active
  363. var tmp=uidRemoved.match(RegExp('(^.*/)'),'');
  364. // XXX it is no longer needed
  365. // globalResourceCardDAVList.loadAddressbookByUID(tmp[1]);
  366. }
  367. break;
  368. }
  369. };
  370. // hide/show contacts in the interface according to contactGroupOrResourceUid or search filter in the interface (contactGroupOrResourceUid==false)
  371. this.applyABFilter=function(contactGroupOrResourceUid, inputForceLoadNext)
  372. {
  373. if(globalCardDAVInitLoad)
  374. return false;
  375. // XXX docasne, potom dame prec
  376. if(!(contactGroupOrResourceUid instanceof Array))
  377. return false;
  378. var vcardGroupOrCollection=[];
  379. for(var i=0;i<contactGroupOrResourceUid.length;i++)
  380. {
  381. if(contactGroupOrResourceUid[i][contactGroupOrResourceUid[i].length-1]=='/')
  382. vcardGroupOrCollection.push({uid: contactGroupOrResourceUid[i]});
  383. else // remember the loaded contact group
  384. {
  385. // required only if we want so support collection unloading
  386. for(var j=0;j<this.vcard_groups[contactGroupOrResourceUid[i].replace(RegExp('/[^/]*$',''),'/')].length;j++)
  387. if(this.vcard_groups[contactGroupOrResourceUid[i].replace(RegExp('/[^/]*$',''),'/')][j].uid==contactGroupOrResourceUid[i])
  388. {
  389. // vcardGroupLoaded bolo zrusene, pozriet co s tym
  390. // vcardGroupOrCollection=this.vcardGroupLoaded=this.vcard_groups[contactGroupOrResourceUid.replace(RegExp('/[^/]*$',''),'/')][j];
  391. vcardGroupOrCollection.push(this.vcard_groups[contactGroupOrResourceUid[i].replace(RegExp('/[^/]*$',''),'/')][j]);
  392. }
  393. }
  394. }
  395. var previousActiveIndex=null; // used to find the nearest contact and set it as selected
  396. if(this.contactLoaded!=null)
  397. var previousActiveUID=this.contactLoaded.uid;
  398. // set all contacts as inactive
  399. for(var i=0;i<this.contacts.length;i++)
  400. if(this.contacts[i].headerOnly==undefined)
  401. {
  402. if(this.contacts[i].uid==previousActiveUID)
  403. previousActiveIndex=i;
  404. this.contacts_hash[this.contacts[i].uid].show=false
  405. this.contacts[i].show=false; // XXX zmenit s5 na false
  406. }
  407. for(var i=0;i<vcardGroupOrCollection.length;i++)
  408. {
  409. if((vcard=vcardGroupOrCollection[i].vcard)==undefined) // collection
  410. {
  411. for(var j=0;j<this.contacts.length;j++)
  412. if(this.contacts[j].headerOnly==undefined)
  413. {
  414. if(this.contacts[j].uid.indexOf(vcardGroupOrCollection[i].uid)==0 && this.contacts[j].search_hide==false)
  415. {
  416. this.contacts[j].show=true;
  417. this.contacts_hash[this.contacts[j].uid].show=true
  418. }
  419. }
  420. }
  421. else // vcard group
  422. {
  423. var vcardUIDList=new Array();
  424. // get the members of the array group
  425. while((vcard_element=vcard.match(vCard.pre['X-ADDRESSBOOKSERVER-MEMBER']))!=null)
  426. {
  427. // parsed (contentline_parse) = [1]->"group.", [2]->"name", [3]->";param;param", [4]->"value"
  428. parsed=vcard_element[0].match(vCard.pre['contentline_parse']);
  429. vcardUIDList[vcardUIDList.length]=parsed[4].replace('urn:uuid:','');
  430. // remove the processed parameter
  431. vcard=vcard.replace(vcard_element[0],'\r\n');
  432. }
  433. // update the contacts' "show" attribute
  434. for(var j=0;j<vcardUIDList.length;j++)
  435. for(var k=0;k<this.contacts.length;k++)
  436. if(this.contacts[k].headerOnly==undefined)
  437. {
  438. vcard_element=this.contacts[k].vcard.match(vCard.pre['contentline_UID']);
  439. if(vcard_element!=null) // only for contacts with UID (non-RFC contacts not contains UID)
  440. {
  441. // parsed (contentline_parse) = [1]->"group.", [2]->"name", [3]->";param;param", [4]->"value"
  442. parsed=vcard_element[0].match(vCard.pre['contentline_parse']);
  443. if(vcardUIDList[j]==parsed[4] && this.contacts[k].search_hide==false)
  444. {
  445. this.contacts[k].show=true;
  446. this.contacts_hash[this.contacts[k].uid].show=true
  447. }
  448. }
  449. }
  450. }
  451. }
  452. var lastActive=null;
  453. var prevHeader=null;
  454. var lastContactForHeader=this.contacts.length-1;
  455. // performance
  456. var tmpListRefChildren=globalRefABListTable.children();
  457. // init displayed columns text length cache
  458. var columnLengths = [];
  459. for(var i=0; i<getDataColumnCount(); i++) {
  460. columnLengths.push([]);
  461. }
  462. // the show attribute is now set, we can make changes in the interface
  463. for(var i=this.contacts.length-1;i>=0;i--)
  464. {
  465. if(this.contacts[i].headerOnly==undefined)
  466. {
  467. // find the previous header index
  468. for(var j=i-1;j>=0;j--)
  469. if(this.contacts[j].headerOnly!=undefined && this.contacts[j].headerOnly==true)
  470. {
  471. prevHeader=j;
  472. break;
  473. }
  474. // performance
  475. var tmpListRefChildren_i=tmpListRefChildren.eq(i);
  476. var tmpListRefChildren_prev=tmpListRefChildren.eq(prevHeader);
  477. var coll_tmp=this.contacts[i].uid.match(RegExp('^(https?://)([^@/]+(?:@[^@/]+)?)@([^/]+)(.*/)([^/]+/)([^/]*)','i'));
  478. var collection_uid=coll_tmp[1]+coll_tmp[2]+'@'+coll_tmp[3]+coll_tmp[4]+coll_tmp[5];
  479. var coll_color=globalResourceCardDAVList.getCollectionByUID(collection_uid).color;
  480. this.contacts[i].color = coll_color;
  481. tmpListRefChildren_i.find('.ablist_item_color').css('background-color', coll_color);
  482. switch(this.contacts[i].show)
  483. {
  484. case false:
  485. tmpListRefChildren_i.css('display','none');
  486. if(tmpListRefChildren_i.hasClass('ablist_item_selected'))
  487. lastActive=i;
  488. var hideHeader=true;
  489. for(j=prevHeader+1;j<=lastContactForHeader;j++)
  490. if(this.contacts[j].show==true)
  491. {
  492. hideHeader=false;
  493. break;
  494. }
  495. if(hideHeader)
  496. tmpListRefChildren_prev.css('display','none');
  497. break;
  498. case true:
  499. // set the contact header to visible
  500. tmpListRefChildren_prev.css('display','');
  501. // set the contact to visible
  502. tmpListRefChildren_i.css('display','');
  503. // save column text length into cache
  504. tmpListRefChildren_i.children().slice(globalFixedContactDataColumnsCount).each(function(ind) {
  505. columnLengths[ind].push($(this).text().length);
  506. });
  507. break;
  508. default:
  509. break;
  510. }
  511. }
  512. else
  513. lastContactForHeader=i-1;
  514. }
  515. setDataColumnsWidth(columnLengths);
  516. // the previously loaded contact is hidden or not exists we need to select a new one
  517. if(inputForceLoadNext==true || $('#vCardEditor').attr('data-editor-state')!='edit' && (lastActive!=null || globalRefABListTable.children('.ablist_item_selected').length==0))
  518. {
  519. var nextCandidateToLoad=null;
  520. // get the nearest candidate to load
  521. // if we can go forward
  522. if(this.contactToReload!=null)
  523. nextCandidateToLoad=this.contactToReload;
  524. else
  525. {
  526. for(j=(previousActiveIndex==null ? 0 : previousActiveIndex);j<this.contacts.length;j++)
  527. if((this.contacts[j].headerOnly==undefined || this.contacts[j].headerOnly==false) && (this.contacts[j].show==true))
  528. {
  529. nextCandidateToLoad=this.contacts[j];
  530. break;
  531. }
  532. // we must go backwards
  533. if(nextCandidateToLoad==null && previousActiveIndex!=null)
  534. {
  535. for(j=previousActiveIndex-1;j>=0;j--)
  536. if((this.contacts[j].headerOnly==undefined || this.contacts[j].headerOnly==false) && (this.contacts[j].show==true))
  537. {
  538. nextCandidateToLoad=this.contacts[j];
  539. break;
  540. }
  541. }
  542. }
  543. // make the contact active
  544. globalRefABListTable.children('.ablist_item.ablist_item_selected').removeClass('ablist_item_selected');
  545. if(nextCandidateToLoad!=null)
  546. {
  547. // prevent re-loading the contact if it is already loaded
  548. if((this.contactToReload!=null||$('#vCardEditor').attr('data-url')!=nextCandidateToLoad.uid) && !globalCardDAVInitLoad)
  549. {
  550. this.loadContactByUID(nextCandidateToLoad.uid);
  551. }
  552. else // because the collection click unselects the active contact we need to re-select it
  553. {
  554. // Make the selected contact active
  555. globalRefABListTable.children('.ablist_item.ablist_item_selected').removeClass('ablist_item_selected');
  556. globalRefABListTable.children('[data-id='+jqueryEscapeSelector(nextCandidateToLoad.uid)+']').addClass('ablist_item_selected');
  557. }
  558. // move scrollbar to ensure that the contact is visible in the interface
  559. if((selected_contact=globalRefABListTable.children('.ablist_item_selected')).length==1)
  560. globalRefABList.scrollTop(globalRefABList.scrollTop()+selected_contact.offset().top-globalRefABList.offset().top-globalRefABList.height()*globalKBNavigationPaddingRate);
  561. }
  562. else
  563. {
  564. this.contactLoaded=null;
  565. $('#ABContactColor').css('background-color', '');
  566. $('#ABContact').html('');
  567. }
  568. }
  569. if(this.contactToReload!=null&& (selected_contact=globalRefABListTable.find('[data-id="'+this.contactToReload.uid+'"]')).length==1)
  570. {
  571. selected_contact.addClass('ablist_item_selected');
  572. globalRefABList.scrollTop(globalRefABList.scrollTop()+selected_contact.offset().top-globalRefABList.offset().top-globalRefABList.height()*globalKBNavigationPaddingRate);
  573. }
  574. }
  575. this.getABCategories=function(returnSorted)
  576. {
  577. var categoriesArr=[];
  578. for(var category in this.contact_categories)
  579. categoriesArr.push(category);
  580. if(returnSorted)
  581. return categoriesArr.sort(function(x,y){return x.customCompare(y,globalSortAlphabet,1,false)});
  582. else
  583. return categoriesArr;
  584. }
  585. this.getABCompanies=function(returnSorted)
  586. {
  587. var companiesArr=[];
  588. for(var company in this.contact_companies)
  589. companiesArr.push(company);
  590. if(returnSorted)
  591. return companiesArr.sort(function(x,y){return x.customCompare(y,globalSortAlphabet,1,false)});
  592. else
  593. return companiesArr;
  594. }
  595. this.getABCompanyDepartments=function(inputCompany)
  596. {
  597. var departmentsArr=[];
  598. if(this.contact_companies[inputCompany]!=undefined)
  599. departmentsArr=this.contact_companies[inputCompany].departments.slice();
  600. return departmentsArr.sort(function(x,y){return x.customCompare(y,globalSortAlphabet,1,false)});
  601. }
  602. // Contact list is not sorted, instead "insert sort" is performed
  603. this.insertContact=function(inputContact, forceReload, disableDOM)
  604. {
  605. // Apple "group" vCards
  606. if(this.isContactGroup(inputContact.vcard))
  607. return this.insertContactGroup(inputContact, forceReload, false);
  608. // check for company contact
  609. inputContact.isCompany=false;
  610. var vcard_element=inputContact.vcard.match(vCard.pre['X-ABShowAs']);
  611. if(vcard_element!=null)
  612. {
  613. // parsed (contentline_parse) = [1]->"group.", [2]->"name", [3]->";param;param", [4]->"value"
  614. parsed=vcard_element[0].match(vCard.pre['contentline_parse']);
  615. if(vcardUnescapeValue(parsed[4]).match(RegExp('^company$','i')))
  616. inputContact.isCompany=true;
  617. }
  618. // check for company contact
  619. if((typeof globalContactsExtVcardToData)=='function')
  620. {
  621. inputContact.isLegacy=false;
  622. var vcard_element=inputContact.vcard.match(RegExp('\r\nX-IsLegacy:.*\r\n', 'mi'));
  623. if(vcard_element!=null)
  624. {
  625. // parsed (contentline_parse) = [1]->"group.", [2]->"name", [3]->";param;param", [4]->"value"
  626. var parsed=vcard_element[0].match(vCard.pre['contentline_parse']);
  627. if(vcardUnescapeValue(parsed[4]).match(RegExp('^(?:yes|1|true)$', 'i')))
  628. inputContact.isLegacy=true;
  629. }
  630. }
  631. // contact UID attr
  632. var vcard_element=inputContact.vcard.match(vCard.pre['contentline_UID']);
  633. if(vcard_element!=null)
  634. {
  635. // parsed (contentline_parse) = [1]->"group.", [2]->"name", [3]->";param;param", [4]->"value"
  636. parsed=vcard_element[0].match(vCard.pre['contentline_parse']);
  637. inputContact.uidattr=vcardUnescapeValue(parsed[4]);
  638. }
  639. else // UID attr is REQUIRED
  640. return false; // invalud vcard
  641. var this_destination=this.contacts;
  642. var this_destination_hash=this.contacts_hash;
  643. var this_destination_hash_uidattr=this.contacts_hash_uidattr;
  644. // search plugin requirement
  645. inputContact.search_hide=false;
  646. // CATEGORIES suggestion
  647. var categoriesArr=(inputContact.categories=='' ? [] : vcardSplitValue(inputContact.categories,','));
  648. var allCategoriesArr=this.getABCategories(false);
  649. // The search funcionality uses this ASCII value (you can add additional data here)
  650. // ORG attribute
  651. var tmp=inputContact.vcard;
  652. var orgArr=[];
  653. var depArr=[];
  654. var tmpCurrentCompany='';
  655. var tmpCurrentDepartment='';
  656. while((vcard_element=tmp.match(vCard.pre['contentline_ORG']))!=null)
  657. {
  658. // parsed (contentline_parse) = [1]->"group.", [2]->"name", [3]->";param;param", [4]->"value"
  659. var parsed=vcard_element[0].match(vCard.pre['contentline_parse']);
  660. var parsed_valArr=vcardSplitValue(parsed[4], ';');
  661. if(isDataColumnDefined('COMPANY')) {
  662. setContactDataColumn(inputContact, 'COMPANY', vcardUnescapeValue(parsed_valArr[0]));
  663. }
  664. if(isDataColumnDefined('DEPARTMENT')) {
  665. setContactDataColumn(inputContact, 'DEPARTMENT', vcardUnescapeValue(parsed_valArr[1]));
  666. }
  667. tmpCurrentCompany=(parsed_valArr[0]==undefined || parsed_valArr[0]=='' ? '' : parsed_valArr[0]);
  668. tmpCurrentDepartment=(parsed_valArr[1]==undefined || parsed_valArr[1]=='' ? '' : parsed_valArr[1]);
  669. if(tmpCurrentCompany!='')
  670. orgArr[orgArr.length]=vcardUnescapeValue(tmpCurrentCompany);
  671. if(tmpCurrentDepartment)
  672. depArr[depArr.length]=vcardUnescapeValue(tmpCurrentDepartment);
  673. // remove the processed parameter
  674. tmp=tmp.replace(vcard_element[0],'\r\n');
  675. }
  676. var allOrgArr=this.getABCompanies(false);
  677. // N attribute
  678. while((vcard_element=tmp.match(vCard.pre['contentline_N']))!=null)
  679. {
  680. // parsed (contentline_parse) = [1]->"group.", [2]->"name", [3]->";param;param", [4]->"value"
  681. var parsed=vcard_element[0].match(vCard.pre['contentline_parse']);
  682. var parsed_valArr=vcardSplitValue(parsed[4],';');
  683. if(isDataColumnDefined('LASTNAME')) {
  684. setContactDataColumn(inputContact, 'LASTNAME', vcardUnescapeValue(parsed_valArr[0]));
  685. }
  686. if(isDataColumnDefined('FIRSTNAME')) {
  687. setContactDataColumn(inputContact, 'FIRSTNAME', vcardUnescapeValue(parsed_valArr[1]));
  688. }
  689. if(isDataColumnDefined('MIDDLENAME')) {
  690. setContactDataColumn(inputContact, 'MIDDLENAME', vcardUnescapeValue(parsed_valArr[2]));
  691. }
  692. if(isDataColumnDefined('PREFIX')) {
  693. setContactDataColumn(inputContact, 'PREFIX', vcardUnescapeValue(parsed_valArr[3]));
  694. }
  695. if(isDataColumnDefined('SUFFIX')) {
  696. setContactDataColumn(inputContact, 'SUFFIX', vcardUnescapeValue(parsed_valArr[4]));
  697. }
  698. // remove the processed parameter
  699. tmp=tmp.replace(vcard_element[0],'\r\n');
  700. }
  701. // NICKNAME attribute
  702. while((vcard_element=tmp.match(vCard.pre['contentline_NICKNAME']))!=null)
  703. {
  704. // parsed (contentline_parse) = [1]->"group.", [2]->"name", [3]->";param;param", [4]->"value"
  705. parsed=vcard_element[0].match(vCard.pre['contentline_parse']);
  706. if(isDataColumnDefined('NICKNAME')) {
  707. setContactDataColumn(inputContact, 'NICKNAME', parsed[4]);
  708. }
  709. // remove the processed parameter
  710. tmp=tmp.replace(vcard_element[0],'\r\n');
  711. }
  712. // X-PHONETIC-LAST-NAME attribute
  713. while((vcard_element=tmp.match(vCard.pre['contentline_X-PHONETIC-LAST-NAME']))!=null)
  714. {
  715. // parsed (contentline_parse) = [1]->"group.", [2]->"name", [3]->";param;param", [4]->"value"
  716. parsed=vcard_element[0].match(vCard.pre['contentline_parse']);
  717. if(isDataColumnDefined('PHONETICLASTNAME')) {
  718. setContactDataColumn(inputContact, 'PHONETICLASTNAME', parsed[4]);
  719. }
  720. // remove the processed parameter
  721. tmp=tmp.replace(vcard_element[0],'\r\n');
  722. }
  723. // X-PHONETIC-FIRST-NAME attribute
  724. while((vcard_element=tmp.match(vCard.pre['contentline_X-PHONETIC-FIRST-NAME']))!=null)
  725. {
  726. // parsed (contentline_parse) = [1]->"group.", [2]->"name", [3]->";param;param", [4]->"value"
  727. parsed=vcard_element[0].match(vCard.pre['contentline_parse']);
  728. if(isDataColumnDefined('PHONETICFIRSTNAME')) {
  729. setContactDataColumn(inputContact, 'PHONETICFIRSTNAME', parsed[4]);
  730. }
  731. // remove the processed parameter
  732. tmp=tmp.replace(vcard_element[0],'\r\n');
  733. }
  734. // BDAY attribute
  735. while((vcard_element=tmp.match(vCard.pre['contentline_BDAY']))!=null)
  736. {
  737. // parsed (contentline_parse) = [1]->"group.", [2]->"name", [3]->";param;param", [4]->"value"
  738. parsed=vcard_element[0].match(vCard.pre['contentline_parse']);
  739. if(isDataColumnDefined('BIRTHDAY')) {
  740. var bday = null;
  741. try {
  742. bday = $.datepicker.parseDate('yy-mm-dd', parsed[4]);
  743. }
  744. catch(e) {
  745. }
  746. if(bday) {
  747. setContactDataColumn(inputContact, 'BIRTHDAY', $.datepicker.formatDate(globalSettings.datepickerformat.value, bday));
  748. }
  749. }
  750. // remove the processed parameter
  751. tmp=tmp.replace(vcard_element[0],'\r\n');
  752. }
  753. // TITLE attribute
  754. while((vcard_element=tmp.match(vCard.pre['contentline_TITLE']))!=null)
  755. {
  756. // parsed (contentline_parse) = [1]->"group.", [2]->"name", [3]->";param;param", [4]->"value"
  757. parsed=vcard_element[0].match(vCard.pre['contentline_parse']);
  758. if(isDataColumnDefined('JOBTITLE')) {
  759. setContactDataColumn(inputContact, 'JOBTITLE', vcardUnescapeValue(parsed[4]));
  760. }
  761. // remove the processed parameter
  762. tmp=tmp.replace(vcard_element[0],'\r\n');
  763. }
  764. // NOTE attribute
  765. while((vcard_element=tmp.match(vCard.pre['contentline_NOTE']))!=null)
  766. {
  767. // parsed (contentline_parse) = [1]->"group.", [2]->"name", [3]->";param;param", [4]->"value"
  768. parsed=vcard_element[0].match(vCard.pre['contentline_parse']);
  769. if(isDataColumnDefined('NOTETEXT')) {
  770. setContactDataColumn(inputContact, 'NOTETEXT', vcardUnescapeValue(parsed[4]));
  771. }
  772. // remove the processed parameter
  773. tmp=tmp.replace(vcard_element[0],'\r\n');
  774. }
  775. // ADR attribute
  776. while((vcard_element=tmp.match(vCard.pre['contentline_ADR']))!=null)
  777. {
  778. // parsed (contentline_parse) = [1]->"group.", [2]->"name", [3]->";param;param", [4]->"value"
  779. var parsed=vcard_element[0].match(vCard.pre['contentline_parse']);
  780. var parsed_valArr=vcardSplitValue(parsed[4],';');
  781. if(isDataColumnDefined('ADDRESS')) {
  782. var unescapedArr = $.map(parsed_valArr, function(el) {
  783. if(el) {
  784. return vcardUnescapeValue(el);
  785. }
  786. });
  787. setContactDataColumn(inputContact, 'ADDRESS', unescapedArr.join(' '), {'TYPE': getParamsFromContentlineParse(tmp, parsed, 'TYPE', 'X-ABLabel', 'address_type_store_as')});
  788. }
  789. // remove the processed parameter
  790. tmp=tmp.replace(vcard_element[0],'\r\n');
  791. }
  792. // TEL attribute
  793. while((vcard_element=tmp.match(vCard.pre['contentline_TEL']))!=null)
  794. {
  795. // parsed (contentline_parse) = [1]->"group.", [2]->"name", [3]->";param;param", [4]->"value"
  796. parsed=vcard_element[0].match(vCard.pre['contentline_parse']);
  797. if(isDataColumnDefined('PHONE')) {
  798. setContactDataColumn(inputContact, 'PHONE', parsed[4], {'TYPE': getParamsFromContentlineParse(tmp, parsed, 'TYPE', 'X-ABLabel', 'phone_type_store_as')});
  799. }
  800. // remove the processed parameter
  801. tmp=tmp.replace(vcard_element[0],'\r\n');
  802. }
  803. // EMAIL attribute
  804. while((vcard_element=tmp.match(vCard.pre['contentline_EMAIL']))!=null)
  805. {
  806. // parsed (contentline_parse) = [1]->"group.", [2]->"name", [3]->";param;param", [4]->"value"
  807. parsed=vcard_element[0].match(vCard.pre['contentline_parse']);
  808. if(isDataColumnDefined('EMAIL')) {
  809. setContactDataColumn(inputContact, 'EMAIL', parsed[4], {'TYPE': getParamsFromContentlineParse(tmp, parsed, 'TYPE', 'X-ABLabel', 'email_type_store_as')});
  810. }
  811. // remove the processed parameter
  812. tmp=tmp.replace(vcard_element[0],'\r\n');
  813. }
  814. // URL attribute
  815. while((vcard_element=tmp.match(vCard.pre['contentline_URL']))!=null)
  816. {
  817. // parsed (contentline_parse) = [1]->"group.", [2]->"name", [3]->";param;param", [4]->"value"
  818. parsed=vcard_element[0].match(vCard.pre['contentline_parse']);
  819. if(isDataColumnDefined('URL')) {
  820. setContactDataColumn(inputContact, 'URL', parsed[4], {'TYPE': getParamsFromContentlineParse(tmp, parsed, 'TYPE', 'X-ABLabel', 'url_type_store_as')});
  821. }
  822. // remove the processed parameter
  823. tmp=tmp.replace(vcard_element[0],'\r\n');
  824. }
  825. // X-ABDATE attribute
  826. while((vcard_element=tmp.match(vCard.pre['contentline_X-ABDATE']))!=null)
  827. {
  828. // parsed (contentline_parse) = [1]->"group.", [2]->"name", [3]->";param;param", [4]->"value"
  829. parsed=vcard_element[0].match(vCard.pre['contentline_parse']);
  830. if(isDataColumnDefined('DATES')) {
  831. var abdate = null;
  832. try {
  833. abdate = $.datepicker.parseDate('yy-mm-dd', parsed[4]);
  834. }
  835. catch(e) {
  836. }
  837. if(abdate) {
  838. setContactDataColumn(inputContact, 'DATES', $.datepicker.formatDate(globalSettings.datepickerformat.value, abdate), {'TYPE': getParamsFromContentlineParse(tmp, parsed, 'TYPE', 'X-ABLabel', 'date_store_as')});
  839. }
  840. }
  841. // remove the processed parameter
  842. tmp=tmp.replace(vcard_element[0],'\r\n');
  843. }
  844. // X-ABRELATEDNAMES attribute
  845. while((vcard_element=tmp.match(vCard.pre['contentline_X-ABRELATEDNAMES']))!=null)
  846. {
  847. // parsed (contentline_parse) = [1]->"group.", [2]->"name", [3]->";param;param", [4]->"value"
  848. parsed=vcard_element[0].match(vCard.pre['contentline_parse']);
  849. if(isDataColumnDefined('RELATED')) {
  850. setContactDataColumn(inputContact, 'RELATED', parsed[4], {'TYPE': getParamsFromContentlineParse(tmp, parsed, 'TYPE', 'X-ABLabel', 'person_type_store_as')});
  851. }
  852. // remove the processed parameter
  853. tmp=tmp.replace(vcard_element[0],'\r\n');
  854. }
  855. // X-SOCIALPROFILE attribute
  856. while((vcard_element=tmp.match(vCard.pre['contentline_X-SOCIALPROFILE']))!=null)
  857. {
  858. // parsed (contentline_parse) = [1]->"group.", [2]->"name", [3]->";param;param", [4]->"value"
  859. parsed=vcard_element[0].match(vCard.pre['contentline_parse']);
  860. if(isDataColumnDefined('PROFILE')) {
  861. setContactDataColumn(inputContact, 'PROFILE', getParamsFromContentlineParse(tmp, parsed, 'X-USER', null, null, true)[0], {'TYPE': getParamsFromContentlineParse(tmp, parsed, 'TYPE', 'X-ABLabel', 'profile_type_store_as')});
  862. }
  863. // remove the processed parameter
  864. tmp=tmp.replace(vcard_element[0],'\r\n');
  865. }
  866. // IMPP attribute
  867. while((vcard_element=tmp.match(vCard.pre['contentline_IMPP']))!=null)
  868. {
  869. // parsed (contentline_parse) = [1]->"group.", [2]->"name", [3]->";param;param", [4]->"value"
  870. parsed=vcard_element[0].match(vCard.pre['contentline_parse']);
  871. if(isDataColumnDefined('IM')) {
  872. setContactDataColumn(inputContact, 'IM', parsed[4].replace(vCard.pre['vcardToData_before_val'], ''), {
  873. 'TYPE': getParamsFromContentlineParse(tmp, parsed, 'TYPE', 'X-ABLabel', 'im_type_store_as'),
  874. 'SERVICE-TYPE': getParamsFromContentlineParse(tmp, parsed, 'X-SERVICE-TYPE', null, 'im_service_type_store_as')
  875. });
  876. }
  877. // remove the processed parameter
  878. tmp=tmp.replace(vcard_element[0],'\r\n');
  879. }
  880. // CATEGORIES attribute (preparsed)
  881. if(isDataColumnDefined('CATEGORIES')) {
  882. setContactDataColumn(inputContact, 'CATEGORIES', inputContact.categories.splitCustom(','));
  883. }
  884. if((inputContact.sortkey=this.getSortKey(inputContact, globalSettings.collectionsort.value || $.map(globalSettings.collectiondisplay.value, function(el) {if($.isPlainObject(el.value)) {return el.value;} else {return [el.value];}}), 0))===false || (inputContact.displayvalue=this.getSortKey(inputContact, globalSettings.collectiondisplay.value, 1))===false)
  885. return false; //invalid vcard
  886. // if company headers are used add also the header to the searchvalue
  887. var companyHeader='';
  888. if(typeof globalGroupContactsByCompanies!='undefined' && globalGroupContactsByCompanies==true)
  889. {
  890. if(tmpCurrentCompany!='' || tmpCurrentDepartment!='')
  891. {
  892. if(typeof (getCRMSortKey)=='function')
  893. companyHeader=getCRMSortKey(inputContact);
  894. else
  895. companyHeader=tmpCurrentCompany+'\u0009'+tmpCurrentDepartment+'\u0009';
  896. }
  897. }
  898. inputContact.searchvalue=(companyHeader+inputContact.displayvalue).multiReplace(globalSearchTransformAlphabet);
  899. // CATEGORIES suggestion
  900. for(var i=0;i<allCategoriesArr.length;i++) // if a contact is changed remove it from previous categories
  901. if(categoriesArr.indexOf(allCategoriesArr[i])==-1)
  902. {
  903. var index=this.contact_categories[allCategoriesArr[i]].indexOf(inputContact.uid);
  904. if(index!=-1)
  905. {
  906. this.contact_categories[allCategoriesArr[i]].splice(index,1);
  907. if(this.contact_categories[allCategoriesArr[i]].length==0)
  908. delete this.contact_categories[allCategoriesArr[i]];
  909. }
  910. }
  911. for(var i=0;i<categoriesArr.length;i++) // add contact to it's categories
  912. this.contact_categories[categoriesArr[i]]=(this.contact_categories[categoriesArr[i]]==undefined ? [] : this.contact_categories[categoriesArr[i]]).concat(inputContact.uid).sort().unique();
  913. // ORG suggestion
  914. for(var i=0;i<allOrgArr.length;i++) // if a contact is changed remove it from previous companies
  915. if(orgArr.indexOf(allOrgArr[i])==-1)
  916. {
  917. var index=this.contact_companies[allOrgArr[i]].uids.indexOf(inputContact.uid);
  918. if(index!=-1)
  919. {
  920. this.contact_companies[allOrgArr[i]].uids.splice(index,1);
  921. if(this.contact_companies[allOrgArr[i]].uids.length==0)
  922. delete this.contact_companies[allOrgArr[i]];
  923. }
  924. }
  925. for(var i=0;i<orgArr.length;i++) // add contact to it's companies
  926. this.contact_companies[orgArr[i]]={uids: (this.contact_companies[orgArr[i]]==undefined ? [] : this.contact_companies[orgArr[i]].uids).concat(inputContact.uid).sort().unique(), departments: (this.contact_companies[orgArr[i]]==undefined ? [] : this.contact_companies[orgArr[i]].departments).concat(depArr).sort().unique()};
  927. var makeActive=null;
  928. // do not insert entry with duplicate UID
  929. if(this_destination_hash[inputContact.uid]!=undefined)
  930. {
  931. var beforeSortKeyChar='';
  932. if(typeof globalGroupContactsByCompanies!='undefined' && globalGroupContactsByCompanies==true && tmpCurrentCompany=='' && tmpCurrentDepartment=='')
  933. beforeSortKeyChar='\u0009';
  934. if(this_destination_hash[inputContact.uid].displayvalue==inputContact.displayvalue && this_destination_hash[inputContact.uid].sortkey==(beforeSortKeyChar+inputContact.sortkey) && this_destination_hash[inputContact.uid].isCompany==inputContact.isCompany && this_destination_hash[inputContact.uid].isLegacy==inputContact.isLegacy)
  935. {
  936. // we perform the normalization here, because we need to check whether the vCard is changed or not
  937. // normalize the vCard when it's loaded first time
  938. if(inputContact.normalized==false)
  939. {
  940. inputContact.normalized=true;
  941. inputContact.vcard=normalizeVcard(additionalRFCFixes(inputContact.vcard));
  942. }
  943. this_destination_hash[inputContact.uid]=inputContact;
  944. this_destination_hash_uidattr[inputContact.uidattr]=inputContact; // hash by UID attr
  945. // if the contact is loaded and the editor is in 'show' state, reload it
  946. if(this.contactLoaded!=null && this.contactLoaded.uid==inputContact.uid && this.contactLoaded.vcard!=inputContact.vcard && $('#vCardEditor').attr('data-editor-state')=='show')
  947. {
  948. this.loadContactByUID(inputContact.uid);
  949. show_editor_message('in', 'message_success', localization[globalInterfaceLanguage].contactConcurrentChange,globalHideInfoMessageAfter);
  950. return 0;
  951. }
  952. else // we are editing the contact or it is not active
  953. return -1;
  954. }
  955. else
  956. {
  957. if(this.contactLoaded!=null && this.contactLoaded.uid==inputContact.uid && forceReload==true)
  958. makeActive=inputContact.uid;
  959. if($('#vCardEditor').attr('data-url')==inputContact.uid)
  960. this.contactToReload=this.contactLoaded;
  961. else
  962. this.contactToReload=null;
  963. // the contact name is changed and must be moved to correct place (we first remove it and then reinsert)
  964. this.removeContact(inputContact.uid,false);
  965. }
  966. }
  967. if(typeof globalGroupContactsByCompanies!='undefined' && globalGroupContactsByCompanies==true)
  968. {
  969. if(tmpCurrentCompany=='' && tmpCurrentDepartment=='')
  970. {
  971. headerValue=headerSortKey='\u0009';
  972. inputContact.sortkey='\u0009'+inputContact.sortkey;
  973. }
  974. else
  975. {
  976. headerValue=vcardUnescapeValue(tmpCurrentCompany)+(tmpCurrentDepartment=='' ? '' : ' ['+vcardUnescapeValue(tmpCurrentDepartment)+']');
  977. if(typeof (getCRMSortKey)== 'function')
  978. headerSortKey=getCRMSortKey(inputContact);
  979. else
  980. headerSortKey=tmpCurrentCompany+'\u0009'+tmpCurrentDepartment+'\u0009';
  981. }
  982. }
  983. else
  984. {
  985. var headerValue='';
  986. // key value for most common non-alphabet characters is defined as '#'
  987. if(inputContact.sortkey[0]!=undefined)
  988. {
  989. var unicodeValue=inputContact.sortkey.charCodeAt(0);
  990. if(unicodeValue<65 || (unicodeValue>90 && unicodeValue<97) || (unicodeValue>122 && unicodeValue<127))
  991. {
  992. headerValue='#';
  993. inputContact.sortkey='#'+inputContact.sortkey;
  994. }
  995. else
  996. headerValue=inputContact.sortkey.charAt(0).toUpperCase();
  997. }
  998. else
  999. {
  1000. headerValue='#';
  1001. inputContact.sortkey='#';
  1002. }
  1003. headerSortKey=headerValue;
  1004. }
  1005. // create the header
  1006. var headerObject={headerOnly: true, sortkey: headerSortKey, displayvalue: headerValue};
  1007. // find the index where to insert the new contact O(n*log(n))
  1008. insertIndex=0;
  1009. low=0;
  1010. high=this_destination.length-1;
  1011. if(this_destination.length>0)
  1012. while(low<high)
  1013. {
  1014. insertIndex=low+Math.round((high-low)/2);
  1015. result=(cmp_str=this_destination[insertIndex].sortkey).customCompare(inputContact.sortkey,globalSortAlphabet, 1, false);
  1016. if(result==-1)
  1017. {
  1018. if(insertIndex+1==this_destination.length-1 && typeof this_destination[insertIndex+1]!='undefined' && (cmp_str=this_destination[insertIndex+1].sortkey).customCompare(inputContact.sortkey, globalSortAlphabet, 1, false)==-1)
  1019. {
  1020. insertIndex+=2;
  1021. break;
  1022. }
  1023. else
  1024. low=++insertIndex;
  1025. }
  1026. else if(result==1)
  1027. {
  1028. if((cmp_str=this_destination[insertIndex-1].sortkey).customCompare(inputContact.sortkey, globalSortAlphabet, 1, false)==-1)
  1029. break;
  1030. else
  1031. high=--insertIndex;
  1032. }
  1033. }
  1034. // check for header existence
  1035. var headerMiss=1;
  1036. for(var i=0;i<this_destination.length;i++)
  1037. if(this_destination[i].headerOnly!=undefined && this_destination[i].headerOnly==true && this_destination[i].displayvalue==headerObject.displayvalue)
  1038. {headerMiss=0; break;}
  1039. // insert the header if not exists
  1040. if(headerMiss)
  1041. this_destination.splice(insertIndex,0,headerObject);
  1042. // insert the contact
  1043. this_destination.splice(insertIndex+headerMiss,0,inputContact);
  1044. // insert reference to the contact into hash for much faster search by UID and UID attr
  1045. this_destination_hash[inputContact.uid]=this_destination[insertIndex+headerMiss];
  1046. this_destination_hash_uidattr[inputContact.uidattr]=this_destination[insertIndex+headerMiss];
  1047. // DOM processing can be disabled for performance (then we use mass DOM operations)
  1048. if(!disableDOM)
  1049. {
  1050. // insert header to interface if not exists
  1051. if(headerMiss)
  1052. {
  1053. var newElement=globalOrigABListHeader.clone();
  1054. newElement.children().text(headerObject.displayvalue);
  1055. if(globalRefABListTable.children().eq(insertIndex).length==0) // if a tbody is completely empty we cannot search using index
  1056. globalRefABListTable.append(newElement);
  1057. else
  1058. globalRefABListTable.children().eq(insertIndex).before(newElement);
  1059. }
  1060. // insert the contact to interface
  1061. var newElement=globalOrigABListItem.clone();
  1062. if(typeof inputContact.isLegacy!='undefined' && inputContact.isLegacy)
  1063. newElement.css('text-decoration','line-through');
  1064. else
  1065. newElement.css('text-decoration','none');
  1066. newElement.attr('data-id', inputContact.uid);
  1067. newElement.children('.ablist_item_color').css('background-color', inputContact.color);
  1068. var columns = getContactDataColumns(inputContact.isCompany);
  1069. for(var i=0; i<columns.length; i++) {
  1070. $('<td>').text(getContactDataColumn(inputContact, columns[i])).appendTo(newElement);
  1071. }
  1072. newElement.click(function() {
  1073. if($(this).hasClass('ablist_item_selected') || globalObjectLoading)
  1074. return false;
  1075. else
  1076. globalAddressbookList.loadContactByUID(this.getAttribute('data-id'));
  1077. });
  1078. // set the company icon
  1079. if(inputContact.isCompany==true)
  1080. newElement.addClass('company');
  1081. if(typeof globalDisableDragAndDrop=='undefined' || globalDisableDragAndDrop!=true)
  1082. newElement.draggable({
  1083. delay: 250,
  1084. revert: 'invalid',
  1085. scroll: false,
  1086. opacity: 0.8,
  1087. stack: '#SystemCardDavMATE',
  1088. containment: '#SystemCardDavMATE',
  1089. appendTo: 'body',
  1090. start: function( event, ui ){
  1091. // disallow on read-only collection
  1092. if(globalResourceCardDAVList.getCollectionPrivByUID($(this).attr('data-id').replace(RegExp('[^/]*$'),''))==true)
  1093. return false;
  1094. },
  1095. helper: function(){
  1096. $('#ResourceCardDAVList').find('.resourceCardDAV.ui-droppable').droppable( 'option', 'accept', false);
  1097. $('#ResourceCardDAVList').find('.group.ui-droppable').droppable( 'option', 'accept', false);
  1098. $('#ResourceCardDAVList').find('.resourceCardDAV[data-id!='+jqueryEscapeSelector($(this).attr('data-id').replace(RegExp('[^/]+$'),''))+'].ui-droppable').droppable( 'option', 'accept', '.ablist_item');
  1099. var myContactGroups=globalAddressbookList.getMyContactGroups($(this).attr('data-id'));
  1100. $('#ResourceCardDAVList').find('.group[data-id^='+jqueryEscapeSelector($(this).attr('data-id').replace(RegExp('[^/]+$'),''))+'].ui-droppable').each(function(index, element){
  1101. if(myContactGroups.indexOf($(element).attr('data-id'))==-1)
  1102. $(element).droppable( 'option', 'accept', '.ablist_item');
  1103. });
  1104. var tmp=$(this).clone();
  1105. tmp.addClass('ablist_item_dragged');
  1106. // we cannot use .css() here, because we need to add !important (problem with Gecko based browsers)
  1107. var tmp_style='max-width: '+$(this).outerWidth()+'px;';
  1108. if($(this).css('background-image')!='none')
  1109. tmp_style+='background-image: url(images/company_s_w.svg) !important;';
  1110. tmp.attr('style', tmp_style);
  1111. return tmp;
  1112. }
  1113. });
  1114. globalRefABListTable.children().eq(insertIndex+headerMiss-1).after(newElement);
  1115. if($('#vCardEditor').attr('data-editor-state')=='edit')
  1116. {
  1117. if((selected_contact=globalRefABListTable.children('.ablist_item_selected')).length==1)
  1118. globalRefABList.scrollTop(globalRefABList.scrollTop()+selected_contact.offset().top-globalRefABList.offset().top-globalRefABList.height()*globalKBNavigationPaddingRate);
  1119. }
  1120. // toto tu asi nahradit zavolanim trigger('click') co vyrazne sprehladni kod
  1121. // =>
  1122. // load the updated contact (because we first deleted it, we need to set it active)
  1123. if(makeActive!=null)
  1124. {
  1125. // make the contact active
  1126. globalRefABListTable.children('.ablist_item.ablist_item_selected').removeClass('ablist_item_selected');
  1127. globalRefABListTable.children().eq(insertIndex+headerMiss).addClass('ablist_item_selected');
  1128. this.loadContactByUID(makeActive);
  1129. }
  1130. }
  1131. }
  1132. this.renderContacs=function()
  1133. {
  1134. var this_destination=this.contacts;
  1135. var this_destination_hash=this.contacts_hash;
  1136. var tmpResultObject=[];
  1137. for(var i=0;i<this_destination.length;i++)
  1138. {
  1139. if(this_destination[i].headerOnly!=undefined && this_destination[i].headerOnly==true)
  1140. {
  1141. var newElement=globalOrigABListHeader.clone();
  1142. newElement.children().text(this_destination[i].displayvalue);
  1143. }
  1144. else
  1145. {
  1146. // insert the contact to interface
  1147. var newElement=globalOrigABListItem.clone();
  1148. if(typeof this_destination[i].isLegacy!='undefined' && this_destination[i].isLegacy)
  1149. newElement.css('text-decoration','line-through');
  1150. else
  1151. newElement.css('text-decoration','none');
  1152. newElement.attr('data-id', this_destination[i].uid);
  1153. newElement.find('.ablist_item_color').css('background-color', this_destination[i].color);
  1154. var columns = getContactDataColumns(this_destination[i].isCompany);
  1155. for(var j=0; j<columns.length; j++) {
  1156. $('<td>').text(getContactDataColumn(this_destination[i], columns[j])).appendTo(newElement);
  1157. }
  1158. for(; j<getDataColumnCount(); j++) {
  1159. $('<td>').appendTo(newElement);
  1160. }
  1161. newElement.click(function() {
  1162. if($(this).hasClass('ablist_item_selected') || globalObjectLoading)
  1163. return false;
  1164. else
  1165. globalAddressbookList.loadContactByUID(this.getAttribute('data-id'));
  1166. });
  1167. // set the company icon
  1168. if(this_destination[i].isCompany==true)
  1169. newElement.addClass('company');
  1170. if(typeof globalDisableDragAndDrop=='undefined' || globalDisableDragAndDrop!=true)
  1171. newElement.draggable({
  1172. delay: 250,
  1173. revert: 'invalid',
  1174. scroll: false,
  1175. opacity: 0.8,
  1176. stack: '#SystemCardDavMATE',
  1177. containment: '#SystemCardDavMATE',
  1178. appendTo: 'body',
  1179. start: function( event, ui ){
  1180. // disallow on read-only collection
  1181. if(globalResourceCardDAVList.getCollectionPrivByUID($(this).attr('data-id').replace(RegExp('[^/]*$'),''))==true)
  1182. return false;
  1183. },
  1184. helper: function(){
  1185. $('#ResourceCardDAVList').find('.resourceCardDAV.ui-droppable').droppable( 'option', 'accept', false);
  1186. $('#ResourceCardDAVList').find('.group.ui-droppable').droppable( 'option', 'accept', false);
  1187. $('#ResourceCardDAVList').find('.resourceCardDAV[data-id!='+jqueryEscapeSelector($(this).attr('data-id').replace(RegExp('[^/]+$'),''))+'].ui-droppable').droppable( 'option', 'accept', '.ablist_item');
  1188. var myContactGroups=globalAddressbookList.getMyContactGroups($(this).attr('data-id'));
  1189. $('#ResourceCardDAVList').find('.group[data-id^='+jqueryEscapeSelector($(this).attr('data-id').replace(RegExp('[^/]+$'),''))+'].ui-droppable').each(function(index, element){
  1190. if(myContactGroups.indexOf($(element).attr('data-id'))==-1)
  1191. $(element).droppable( 'option', 'accept', '.ablist_item');
  1192. });
  1193. var tmp=$(this).clone();
  1194. tmp.addClass('ablist_item_dragged');
  1195. // we cannot use .css() here, because we need to add !important (problem with Gecko based browsers)
  1196. var tmp_style='max-width: '+$(this).outerWidth()+'px;';
  1197. if($(this).css('background-image')!='none')
  1198. tmp_style+='background-image: url(images/company_s_w.svg) !important;';
  1199. tmp.attr('style', tmp_style);
  1200. return tmp;
  1201. }
  1202. });
  1203. }
  1204. tmpResultObject.push(newElement);
  1205. }
  1206. globalRefABListTable.empty().append(tmpResultObject);
  1207. }
  1208. this.removeContact=function(inputUid, loadNext, isFromPUT)
  1209. {
  1210. if(!(inputUid instanceof Array))
  1211. inputUid=[inputUid];
  1212. var tmpRex=new RegExp('/[^/]*$','');
  1213. // Apple "group" vCards
  1214. for(var i=inputUid.length-1;i>=0;i--)
  1215. for(var j=this.vcard_groups[inputUid[i].replace(tmpRex,'/')].length-1;j>=0;j--)
  1216. if(inputUid.indexOf(this.vcard_groups[inputUid[i].replace(tmpRex,'/')][j].uid)!=-1)
  1217. return this.removeContactGroup(inputUid[i], loadNext);
  1218. for(var i=this.contacts.length-1;i>=0;i--)
  1219. if(this.contacts[i]!=undefined&&inputUid.indexOf(this.contacts[i].uid)!=-1)
  1220. {
  1221. var inUID=this.contacts[i].uid;
  1222. // CATEGORIES suggestion
  1223. var categoriesArr=vcardSplitValue(this.contacts[i].categories,',');
  1224. for(var j=0;j<categoriesArr.length;j++)
  1225. if(this.contact_categories[categoriesArr[j]]!=undefined)
  1226. {
  1227. var index=this.contact_categories[categoriesArr[j]].indexOf(this.contacts[i].uid);
  1228. if(index!=-1)
  1229. {
  1230. this.contact_categories[categoriesArr[j]].splice(index,1);
  1231. if(this.contact_categories[categoriesArr[j]].length==0)
  1232. delete this.contact_categories[categoriesArr[j]];
  1233. }
  1234. }
  1235. // ORG suggestion
  1236. var tmp=this.contacts[i].vcard;
  1237. var orgArr=[];
  1238. while((vcard_element=tmp.match(vCard.pre['contentline_ORG']))!=null)
  1239. {
  1240. // parsed (contentline_parse) = [1]->"group.", [2]->"name", [3]->";param;param", [4]->"value"
  1241. parsed=vcard_element[0].match(vCard.pre['contentline_parse']);
  1242. orgArr[orgArr.length]=vcardUnescapeValue(vcardSplitValue(parsed[4],';')[0]);
  1243. // remove the processed parameter
  1244. tmp=tmp.replace(vcard_element[0],'\r\n');
  1245. }
  1246. for(var j=0;j<orgArr.length;j++)
  1247. if(this.contact_companies[orgArr[j]]!=undefined /* there is no suggestion for '' company */ && this.contact_companies[orgArr[j]].uids!=undefined)
  1248. {
  1249. var index=this.contact_companies[orgArr[j]].uids.indexOf(this.contacts[i].uid);
  1250. if(index!=-1)
  1251. {
  1252. this.contact_companies[orgArr[j]].uids.splice(index,1);
  1253. if(this.contact_companies[orgArr[j]].uids.length==0)
  1254. delete this.contact_companies[orgArr[j]];
  1255. }
  1256. }
  1257. var nextCandidateToLoad=null;
  1258. var item=globalRefABListTable.find('[data-id^="'+jqueryEscapeSelector(this.contacts[i].uid)+'"]');
  1259. // get the nearest candidate to load
  1260. // if we can go forward
  1261. for(j=i+1;j<this.contacts.length;j++)
  1262. if(this.contacts[j].headerOnly!=true && (typeof isFromPUT!='undefined' || this.contacts[j].show==true))
  1263. {
  1264. nextCandidateToLoad=this.contacts[j];
  1265. break;
  1266. }
  1267. // we must go backwards
  1268. if(nextCandidateToLoad==null)
  1269. {
  1270. for(j=i-1;j>=0;j--)
  1271. if(this.contacts[j].headerOnly!=true && this.contacts[j].show==true)
  1272. {
  1273. nextCandidateToLoad=this.contacts[j];
  1274. break;
  1275. }
  1276. }
  1277. // remove the item
  1278. item.remove();
  1279. this.contacts.splice(i,1);
  1280. if(this.contacts_hash[inUID]!=undefined)
  1281. {
  1282. delete this.contacts_hash_uidattr[this.contacts_hash[inUID].uidattr];
  1283. delete this.contacts_hash[inUID];
  1284. }
  1285. else if(this.companies_hash[inUID]!=undefined)
  1286. {
  1287. delete this.companies_hash_uidattr[this.contacts_hash[inUID].uidattr];
  1288. delete this.companies_hash[inUID];
  1289. }
  1290. // remove the header if there is no more contact
  1291. var removeHeader=true;
  1292. var prevHeader=null;
  1293. // find the previous header index
  1294. for(var j=i-1;j>=0;j--)
  1295. if(this.contacts[j].headerOnly!=undefined && this.contacts[j].headerOnly==true)
  1296. {
  1297. prevHeader=j;
  1298. break;
  1299. }
  1300. // check for contact existence for the found header
  1301. if((prevHeader+1)<this.contacts.length && (this.contacts[prevHeader+1].headerOnly==undefined || this.contacts[prevHeader+1].headerOnly!=true))
  1302. removeHeader=false;
  1303. // remove the header
  1304. if(removeHeader==true)
  1305. {
  1306. globalRefABListTable.children().eq(prevHeader).remove();
  1307. this.contacts.splice(prevHeader,1);
  1308. }
  1309. // hide header if there is no more visible contacts
  1310. var hideHeader=true;
  1311. for(j=prevHeader+1;j<this.contacts.length && (this.contacts[j].headerOnly==undefined || this.contacts[j].headerOnly!=true);j++)
  1312. if(this.contacts[j].show==true)
  1313. {
  1314. hideHeader=false;
  1315. break;
  1316. }
  1317. if(hideHeader)
  1318. globalRefABListTable.children().eq(prevHeader).css('display','none');
  1319. // update the active search
  1320. if(globalQs!=null)
  1321. globalQs.cache();
  1322. // load next contact
  1323. if(loadNext && this.contactLoaded!=null)
  1324. {
  1325. if(nextCandidateToLoad!=null)
  1326. this.loadContactByUID(nextCandidateToLoad.uid);
  1327. else
  1328. {
  1329. this.contactLoaded=null;
  1330. $('#ABContactColor').css('background-color', '');
  1331. $('#ABContact').html('');
  1332. }
  1333. }
  1334. }
  1335. }
  1336. this.checkAndTouchIfExists=function(inputUID,inputEtag,inputTimestamp)
  1337. {
  1338. if(this.contacts_hash[inputUID]!=undefined)
  1339. {
  1340. this.contacts_hash[inputUID].timestamp=inputTimestamp;
  1341. if(this.contacts_hash[inputUID].etag==inputEtag)
  1342. return true;
  1343. else
  1344. return false;
  1345. }
  1346. else
  1347. return false;
  1348. }
  1349. this.removeOldContacts=function(inputUidBase, inputTimestamp)
  1350. {
  1351. for(var i=this.contacts.length-1;i>=0;i--)
  1352. if(this.contacts[i]!=undefined /* because the header can be deleted with the contact */ && this.contacts[i].timestamp!=undefined && this.contacts[i].uid.indexOf(inputUidBase)==0 && this.contacts[i].timestamp<inputTimestamp)
  1353. this.removeContact(this.contacts[i].uid, true);
  1354. }
  1355. this.loadContactByUID=function(inputUID)
  1356. {
  1357. // find the inputUID contact
  1358. if(this.contacts_hash[inputUID]!=undefined)
  1359. {
  1360. globalObjectLoading=true; // temporary disable keyboard navigation
  1361. // normalize the vCard when it's loaded first time
  1362. if(this.contacts_hash[inputUID].normalized==false)
  1363. {
  1364. this.contacts_hash[inputUID].normalized=true;
  1365. this.contacts_hash[inputUID].vcard=normalizeVcard(additionalRFCFixes(this.contacts_hash[inputUID].vcard));
  1366. }
  1367. var is_readonly=globalResourceCardDAVList.getCollectionPrivByUID(this.contacts_hash[inputUID].uid.replace(RegExp('[^/]*$'),''));
  1368. var loadContact=this.contactLoaded=this.contacts_hash[inputUID];
  1369. // CardDAVeditor_cleanup(false, this.contacts_hash[inputUID].isCompany); // editor initialization
  1370. if(vcardToData(loadContact, is_readonly, this.contacts_hash[inputUID].isCompany, 'hide', []))
  1371. $('#EditorBox').fadeTo(0, 1, function(){ /* 0 => no animation */
  1372. globalObjectLoading=false; // re-enable keyboard navigation
  1373. });
  1374. else
  1375. {
  1376. $('#ABContactColor').css('background-color', '');
  1377. $('#ABContact').empty();
  1378. globalDisableAnimationMessageHiding='contactRfcNotCompliant';
  1379. var tmpTime=show_editor_message('out','message_error', localization[globalInterfaceLanguage].contactRfcNotCompliant, globalHideInfoMessageAfter);
  1380. setTimeout(function(){globalObjectLoading=false;}, tmpTime); // re-enable keyboard navigation
  1381. }
  1382. // Make the selected contact active
  1383. globalRefABListTable.children('.ablist_item.ablist_item_selected').removeClass('ablist_item_selected');
  1384. globalRefABListTable.children('[data-id='+jqueryEscapeSelector(this.contacts_hash[inputUID].uid)+']').addClass('ablist_item_selected');
  1385. this.contactToReload=null;
  1386. if(globalRefABListTable.children('[data-id='+jqueryEscapeSelector(this.contacts_hash[inputUID].uid)+']:visible').length>0&&$('#ABInMessageEditBox').css('display')!='none')
  1387. {
  1388. animate_message('#ABInMessageEditBox', '#ABInMessageTextEditBox', 0, '-=');
  1389. $('#ABInMessageEditBox').css('display','');
  1390. }
  1391. else if(globalRefABListTable.children('[data-id='+jqueryEscapeSelector(this.contacts_hash[inputUID].uid)+']:visible').length==0&&$('#ABInMessageEditBox').css('display')=='none')
  1392. {
  1393. this.contactToReload=this.contacts_hash[inputUID];
  1394. globalDisableAnimationMessageHiding='errContactHidden';
  1395. $('#ABInMessageEditBox').css('display','block');
  1396. $('#ABInMessageTextEditBox').attr('class','message_success');
  1397. $('#ABInMessageTextEditBox').text(localization[globalInterfaceLanguage][globalDisableAnimationMessageHiding]);
  1398. animate_message('#ABInMessageEditBox', '#ABInMessageTextEditBox', globalHideInfoMessageAfter);
  1399. }
  1400. if($('#ResourceCardDAVListOverlay').is(':visible'))
  1401. {
  1402. if($('#ABContactOverlay').is(':visible'))
  1403. {
  1404. var animation = 400;
  1405. var duration = globalHideInfoMessageAfter + 2*animation;
  1406. setTimeout(function(){
  1407. $('#ResourceCardDAVListOverlay').fadeOut(animation);
  1408. $('#ABListOverlay').fadeOut(animation,function(){});
  1409. $('#ABContactOverlay').fadeOut(animation,function(){globalRefAddContact.prop('disabled',false);});
  1410. },duration-animation);
  1411. }
  1412. else
  1413. {
  1414. $('#ResourceCardDAVListOverlay').fadeOut(globalEditorFadeAnimation);
  1415. $('#ABListOverlay').fadeOut(globalEditorFadeAnimation,function(){});
  1416. }
  1417. }
  1418. }
  1419. else
  1420. {
  1421. $('#ABContactColor').css('background-color', '');
  1422. $('#ABContact').empty();
  1423. // CardDAVeditor_cleanup(false, false); // editor initialization
  1424. }
  1425. checkContactFormScrollBar();
  1426. }
  1427. this.loadContactByVcard=function(vcard, color, isCompany, inputEditorMode, inputEditorLockedEntries)
  1428. {
  1429. // sem callback pre index.html a v pripade ak pridavame usera (nie firmu) pridat do vcard prislusny atribut
  1430. if(typeof(globalContactsExtLoadByVcardBefore)=='function')
  1431. vcard=globalContactsExtLoadByVcardBefore(vcard, isCompany);
  1432. var loadContact=new Object();
  1433. loadContact.vcard=vcard;
  1434. loadContact.isCompany=isCompany;
  1435. loadContact.color=color;
  1436. globalObjectLoading=true; // temporary disable keyboard navigation
  1437. if(vcardToData(loadContact, false /* XXX check this */, isCompany, inputEditorMode, inputEditorLockedEntries))
  1438. $('#EditorBox').fadeTo(0, 1, function(){ /* 0 => no animation */
  1439. // append the UID of previous contact into "data-id" for "cancel" functionality
  1440. $('#vCardEditor').find('[data-type="cancel"]').attr('data-id', globalAddressbookList.getLoadedContactUID());
  1441. this.contactLoaded=null; // do not do this earlier
  1442. globalObjectLoading=false; // re-enable keyboard navigation
  1443. });
  1444. else
  1445. {
  1446. // todo: replace with icon or text in the editor div
  1447. globalDisableAnimationMessageHiding='contactRfcNotCompliant';
  1448. show_editor_message('out', 'message_error', localization[globalInterfaceLanguage].contactRfcNotCompliant, globalHideInfoMessageAfter);
  1449. this.contactLoaded=null; // do not do this earlier
  1450. globalObjectLoading=false; // re-enable keyboard navigation
  1451. }
  1452. checkContactFormScrollBar();
  1453. }
  1454. // DONE
  1455. this.getContactByUID=function(inputUID)
  1456. {
  1457. // find the inputUID contact
  1458. if(this.contacts_hash[inputUID]!=undefined)
  1459. return this.contacts_hash[inputUID];
  1460. else
  1461. return null;
  1462. }
  1463. // DONE
  1464. this.getContactGroupByUID=function(inputUID)
  1465. {
  1466. var collectionUID=inputUID.replace(RegExp('[^/]*$'),'');
  1467. for(var i=0;i<this.vcard_groups[collectionUID].length;i++)
  1468. {
  1469. if(this.vcard_groups[collectionUID][i].uid==inputUID)
  1470. return this.vcard_groups[collectionUID][i];
  1471. }
  1472. return null;
  1473. }
  1474. }