" )
838 | .addClass( "ui-dialog-buttonpane ui-widget-content ui-helper-clearfix" );
839 |
840 | this.uiButtonSet = $( "
" )
841 | .addClass( "ui-dialog-buttonset" )
842 | .appendTo( this.uiDialogButtonPane );
843 |
844 | this._createButtons();
845 | },
846 |
847 | _createButtons: function() {
848 | var that = this,
849 | buttons = this.options.buttons;
850 |
851 | // if we already have a button pane, remove it
852 | this.uiDialogButtonPane.remove();
853 | this.uiButtonSet.empty();
854 |
855 | if ( $.isEmptyObject( buttons ) || ($.isArray( buttons ) && !buttons.length) ) {
856 | this.uiDialog.removeClass( "ui-dialog-buttons" );
857 | return;
858 | }
859 |
860 | $.each( buttons, function( name, props ) {
861 | var click, buttonOptions;
862 | props = $.isFunction( props ) ?
863 | { click: props, text: name } :
864 | props;
865 | // Default to a non-submitting button
866 | props = $.extend( { type: "button" }, props );
867 | // Change the context for the click callback to be the main element
868 | click = props.click;
869 | props.click = function() {
870 | click.apply( that.element[ 0 ], arguments );
871 | };
872 | buttonOptions = {
873 | icons: props.icons,
874 | text: props.showText
875 | };
876 | delete props.icons;
877 | delete props.showText;
878 | $( "
", props )
879 | .button( buttonOptions )
880 | .appendTo( that.uiButtonSet );
881 | });
882 | this.uiDialog.addClass( "ui-dialog-buttons" );
883 | this.uiDialogButtonPane.appendTo( this.uiDialog );
884 | },
885 |
886 | _makeDraggable: function() {
887 | var that = this,
888 | options = this.options;
889 |
890 | function filteredUi( ui ) {
891 | return {
892 | position: ui.position,
893 | offset: ui.offset
894 | };
895 | }
896 |
897 | this.uiDialog.draggable({
898 | cancel: ".ui-dialog-content, .ui-dialog-titlebar-close",
899 | handle: ".ui-dialog-titlebar",
900 | containment: "document",
901 | start: function( event, ui ) {
902 | $( this ).addClass( "ui-dialog-dragging" );
903 | that._blockFrames();
904 | that._trigger( "dragStart", event, filteredUi( ui ) );
905 | },
906 | drag: function( event, ui ) {
907 | that._trigger( "drag", event, filteredUi( ui ) );
908 | },
909 | stop: function( event, ui ) {
910 | var left = ui.offset.left - that.document.scrollLeft(),
911 | top = ui.offset.top - that.document.scrollTop();
912 |
913 | options.position = {
914 | my: "left top",
915 | at: "left" + (left >= 0 ? "+" : "") + left + " " +
916 | "top" + (top >= 0 ? "+" : "") + top,
917 | of: that.window
918 | };
919 | $( this ).removeClass( "ui-dialog-dragging" );
920 | that._unblockFrames();
921 | that._trigger( "dragStop", event, filteredUi( ui ) );
922 | }
923 | });
924 | },
925 |
926 | _makeResizable: function() {
927 | var that = this,
928 | options = this.options,
929 | handles = options.resizable,
930 | // .ui-resizable has position: relative defined in the stylesheet
931 | // but dialogs have to use absolute or fixed positioning
932 | position = this.uiDialog.css("position"),
933 | resizeHandles = typeof handles === "string" ?
934 | handles :
935 | "n,e,s,w,se,sw,ne,nw";
936 |
937 | function filteredUi( ui ) {
938 | return {
939 | originalPosition: ui.originalPosition,
940 | originalSize: ui.originalSize,
941 | position: ui.position,
942 | size: ui.size
943 | };
944 | }
945 |
946 | this.uiDialog.resizable({
947 | cancel: ".ui-dialog-content",
948 | containment: "document",
949 | alsoResize: this.element,
950 | maxWidth: options.maxWidth,
951 | maxHeight: options.maxHeight,
952 | minWidth: options.minWidth,
953 | minHeight: this._minHeight(),
954 | handles: resizeHandles,
955 | start: function( event, ui ) {
956 | $( this ).addClass( "ui-dialog-resizing" );
957 | that._blockFrames();
958 | that._trigger( "resizeStart", event, filteredUi( ui ) );
959 | },
960 | resize: function( event, ui ) {
961 | that._trigger( "resize", event, filteredUi( ui ) );
962 | },
963 | stop: function( event, ui ) {
964 | var offset = that.uiDialog.offset(),
965 | left = offset.left - that.document.scrollLeft(),
966 | top = offset.top - that.document.scrollTop();
967 |
968 | options.height = that.uiDialog.height();
969 | options.width = that.uiDialog.width();
970 | options.position = {
971 | my: "left top",
972 | at: "left" + (left >= 0 ? "+" : "") + left + " " +
973 | "top" + (top >= 0 ? "+" : "") + top,
974 | of: that.window
975 | };
976 | $( this ).removeClass( "ui-dialog-resizing" );
977 | that._unblockFrames();
978 | that._trigger( "resizeStop", event, filteredUi( ui ) );
979 | }
980 | })
981 | .css( "position", position );
982 | },
983 |
984 | _trackFocus: function() {
985 | this._on( this.widget(), {
986 | focusin: function( event ) {
987 | this._makeFocusTarget();
988 | this._focusedElement = $( event.target );
989 | }
990 | });
991 | },
992 |
993 | _makeFocusTarget: function() {
994 | this._untrackInstance();
995 | this._trackingInstances().unshift( this );
996 | },
997 |
998 | _untrackInstance: function() {
999 | var instances = this._trackingInstances(),
1000 | exists = $.inArray( this, instances );
1001 | if ( exists !== -1 ) {
1002 | instances.splice( exists, 1 );
1003 | }
1004 | },
1005 |
1006 | _trackingInstances: function() {
1007 | var instances = this.document.data( "ui-dialog-instances" );
1008 | if ( !instances ) {
1009 | instances = [];
1010 | this.document.data( "ui-dialog-instances", instances );
1011 | }
1012 | return instances;
1013 | },
1014 |
1015 | _minHeight: function() {
1016 | var options = this.options;
1017 |
1018 | return options.height === "auto" ?
1019 | options.minHeight :
1020 | Math.min( options.minHeight, options.height );
1021 | },
1022 |
1023 | _position: function() {
1024 | // Need to show the dialog to get the actual offset in the position plugin
1025 | var isVisible = this.uiDialog.is( ":visible" );
1026 | if ( !isVisible ) {
1027 | this.uiDialog.show();
1028 | }
1029 | this.uiDialog.position( this.options.position );
1030 | if ( !isVisible ) {
1031 | this.uiDialog.hide();
1032 | }
1033 | },
1034 |
1035 | _setOptions: function( options ) {
1036 | var that = this,
1037 | resize = false,
1038 | resizableOptions = {};
1039 |
1040 | $.each( options, function( key, value ) {
1041 | that._setOption( key, value );
1042 |
1043 | if ( key in that.sizeRelatedOptions ) {
1044 | resize = true;
1045 | }
1046 | if ( key in that.resizableRelatedOptions ) {
1047 | resizableOptions[ key ] = value;
1048 | }
1049 | });
1050 |
1051 | if ( resize ) {
1052 | this._size();
1053 | this._position();
1054 | }
1055 | if ( this.uiDialog.is( ":data(ui-resizable)" ) ) {
1056 | this.uiDialog.resizable( "option", resizableOptions );
1057 | }
1058 | },
1059 |
1060 | _setOption: function( key, value ) {
1061 | var isDraggable, isResizable,
1062 | uiDialog = this.uiDialog;
1063 |
1064 | if ( key === "dialogClass" ) {
1065 | uiDialog
1066 | .removeClass( this.options.dialogClass )
1067 | .addClass( value );
1068 | }
1069 |
1070 | if ( key === "disabled" ) {
1071 | return;
1072 | }
1073 |
1074 | this._super( key, value );
1075 |
1076 | if ( key === "appendTo" ) {
1077 | this.uiDialog.appendTo( this._appendTo() );
1078 | }
1079 |
1080 | if ( key === "buttons" ) {
1081 | this._createButtons();
1082 | }
1083 |
1084 | if ( key === "closeText" ) {
1085 | this.uiDialogTitlebarClose.button({
1086 | // Ensure that we always pass a string
1087 | label: "" + value
1088 | });
1089 | }
1090 |
1091 | if ( key === "draggable" ) {
1092 | isDraggable = uiDialog.is( ":data(ui-draggable)" );
1093 | if ( isDraggable && !value ) {
1094 | uiDialog.draggable( "destroy" );
1095 | }
1096 |
1097 | if ( !isDraggable && value ) {
1098 | this._makeDraggable();
1099 | }
1100 | }
1101 |
1102 | if ( key === "position" ) {
1103 | this._position();
1104 | }
1105 |
1106 | if ( key === "resizable" ) {
1107 | // currently resizable, becoming non-resizable
1108 | isResizable = uiDialog.is( ":data(ui-resizable)" );
1109 | if ( isResizable && !value ) {
1110 | uiDialog.resizable( "destroy" );
1111 | }
1112 |
1113 | // currently resizable, changing handles
1114 | if ( isResizable && typeof value === "string" ) {
1115 | uiDialog.resizable( "option", "handles", value );
1116 | }
1117 |
1118 | // currently non-resizable, becoming resizable
1119 | if ( !isResizable && value !== false ) {
1120 | this._makeResizable();
1121 | }
1122 | }
1123 |
1124 | if ( key === "title" ) {
1125 | this._title( this.uiDialogTitlebar.find( ".ui-dialog-title" ) );
1126 | }
1127 | },
1128 |
1129 | _size: function() {
1130 | // If the user has resized the dialog, the .ui-dialog and .ui-dialog-content
1131 | // divs will both have width and height set, so we need to reset them
1132 | var nonContentHeight, minContentHeight, maxContentHeight,
1133 | options = this.options;
1134 |
1135 | // Reset content sizing
1136 | this.element.show().css({
1137 | width: "auto",
1138 | minHeight: 0,
1139 | maxHeight: "none",
1140 | height: 0
1141 | });
1142 |
1143 | if ( options.minWidth > options.width ) {
1144 | options.width = options.minWidth;
1145 | }
1146 |
1147 | // reset wrapper sizing
1148 | // determine the height of all the non-content elements
1149 | nonContentHeight = this.uiDialog.css({
1150 | height: "auto",
1151 | width: options.width
1152 | })
1153 | .outerHeight();
1154 | minContentHeight = Math.max( 0, options.minHeight - nonContentHeight );
1155 | maxContentHeight = typeof options.maxHeight === "number" ?
1156 | Math.max( 0, options.maxHeight - nonContentHeight ) :
1157 | "none";
1158 |
1159 | if ( options.height === "auto" ) {
1160 | this.element.css({
1161 | minHeight: minContentHeight,
1162 | maxHeight: maxContentHeight,
1163 | height: "auto"
1164 | });
1165 | } else {
1166 | this.element.height( Math.max( 0, options.height - nonContentHeight ) );
1167 | }
1168 |
1169 | if ( this.uiDialog.is( ":data(ui-resizable)" ) ) {
1170 | this.uiDialog.resizable( "option", "minHeight", this._minHeight() );
1171 | }
1172 | },
1173 |
1174 | _blockFrames: function() {
1175 | this.iframeBlocks = this.document.find( "iframe" ).map(function() {
1176 | var iframe = $( this );
1177 |
1178 | return $( "
" )
1179 | .css({
1180 | position: "absolute",
1181 | width: iframe.outerWidth(),
1182 | height: iframe.outerHeight()
1183 | })
1184 | .appendTo( iframe.parent() )
1185 | .offset( iframe.offset() )[0];
1186 | });
1187 | },
1188 |
1189 | _unblockFrames: function() {
1190 | if ( this.iframeBlocks ) {
1191 | this.iframeBlocks.remove();
1192 | delete this.iframeBlocks;
1193 | }
1194 | },
1195 |
1196 | _allowInteraction: function( event ) {
1197 | if ( $( event.target ).closest( ".ui-dialog" ).length ) {
1198 | return true;
1199 | }
1200 |
1201 | // TODO: Remove hack when datepicker implements
1202 | // the .ui-front logic (#8989)
1203 | return !!$( event.target ).closest( ".ui-datepicker" ).length;
1204 | },
1205 |
1206 | _createOverlay: function() {
1207 | if ( !this.options.modal ) {
1208 | return;
1209 | }
1210 |
1211 | // We use a delay in case the overlay is created from an
1212 | // event that we're going to be cancelling (#2804)
1213 | var isOpening = true;
1214 | this._delay(function() {
1215 | isOpening = false;
1216 | });
1217 |
1218 | if ( !this.document.data( "ui-dialog-overlays" ) ) {
1219 |
1220 | // Prevent use of anchors and inputs
1221 | // Using _on() for an event handler shared across many instances is
1222 | // safe because the dialogs stack and must be closed in reverse order
1223 | this._on( this.document, {
1224 | focusin: function( event ) {
1225 | if ( isOpening ) {
1226 | return;
1227 | }
1228 |
1229 | if ( !this._allowInteraction( event ) ) {
1230 | event.preventDefault();
1231 | this._trackingInstances()[ 0 ]._focusTabbable();
1232 | }
1233 | }
1234 | });
1235 | }
1236 |
1237 | this.overlay = $( "
" )
1238 | .addClass( "ui-widget-overlay ui-front" )
1239 | .appendTo( this._appendTo() );
1240 | this._on( this.overlay, {
1241 | mousedown: "_keepFocus"
1242 | });
1243 | this.document.data( "ui-dialog-overlays",
1244 | (this.document.data( "ui-dialog-overlays" ) || 0) + 1 );
1245 | },
1246 |
1247 | _destroyOverlay: function() {
1248 | if ( !this.options.modal ) {
1249 | return;
1250 | }
1251 |
1252 | if ( this.overlay ) {
1253 | var overlays = this.document.data( "ui-dialog-overlays" ) - 1;
1254 |
1255 | if ( !overlays ) {
1256 | this.document
1257 | .unbind( "focusin" )
1258 | .removeData( "ui-dialog-overlays" );
1259 | } else {
1260 | this.document.data( "ui-dialog-overlays", overlays );
1261 | }
1262 |
1263 | this.overlay.remove();
1264 | this.overlay = null;
1265 | }
1266 | }
1267 | });
1268 |
1269 |
1270 |
1271 | }));
--------------------------------------------------------------------------------
/infcloud/carddavmate.config.js:
--------------------------------------------------------------------------------
1 | /*
2 | CardDavMATE - the open source CardDAV Web Client
3 | Copyright (C) 2011-2015
4 | Jan Mate
5 | Andrej Lezo
6 | Matej Mihalik
7 |
8 | This program is free software: you can redistribute it and/or modify
9 | it under the terms of the GNU Affero General Public License as
10 | published by the Free Software Foundation, either version 3 of the
11 | License, or (at your option) any later version.
12 |
13 | This program is distributed in the hope that it will be useful,
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 | GNU Affero General Public License for more details.
17 |
18 | You should have received a copy of the GNU Affero General Public License
19 | along with this program. If not, see .
20 | */
21 |
22 |
23 | // NOTE: see readme.txt before you start to configure this client!
24 |
25 |
26 | // NOTE: do not forget to execute the cache_update.sh script every time you
27 | // update this configuration file or any other files (otherwise your browser
28 | // will use the previous version of files stored in HTML5 cache). Alternatively
29 | // you can update the cache.manifest manually - edit the second line beginning
30 | // with "#V 20" to anything else (this file simple needs "some" change)
31 |
32 |
33 | // Supported setup types (use ONE of them):
34 | // a.) globalAccountSettings => username and password is hardcoded
35 | // in config.js, automatic login without the login screen
36 | // - advantages: fast login process = no username/password is required
37 | // - disadvantages: username/password is visible in your config.js, so
38 | // this type of setup is recommended ONLY for intranet/home users
39 | // b.) globalNetworkCheckSettings => standard setup with login screen
40 | // - advantages: username/password is required (no visible
41 | // username/password in config.js)
42 | // - disadvantages: if a user enters wrong username/password then
43 | // the browser will show authentication popup window (it is NOT
44 | // possible to disable it in JavaScript; see the next option)
45 | // c.) globalNetworkAccountSettings => advanced setup with login screen
46 | // - advantages: no authentication popup if you enter wrong username/
47 | // password, dynamic XML configuration generator (you can generate
48 | // different configurations for your users /by modifying the "auth"
49 | // module configuration or the PHP code itself/)
50 | // - disadvantages: requires PHP >= 5.3 and additional configuration,
51 | // only basic http authentication is supported => always use https!
52 | //
53 | //
54 | // What is a "principal URL"? => Check you server documentation!
55 | // - "principal URL" is NOT "collection URL"
56 | // - this client automatically detects collections for "principal URL"
57 | // - PROPER "principal URL" looks like:
58 | // https://server.com:8443/principals/users/USER/
59 | // https://server.com:8443/caldav.php/USER/
60 | // - INVALID principal URL looks like:
61 | // https://server.com:8443/principals/users/USER/collection/
62 | // => this is a collection URL
63 | // https://server.com:8443/caldav.php/USER/collection/
64 | // => this is a collection URL
65 | // https://server.com:8443/principals/users/USER
66 | // => missing trailing '/'
67 | // https://server.com:8443/caldav.php/USER
68 | // => missing trailing '/'
69 | // /caldav.php/USER/
70 | // => relative URL instead of full URL
71 | //
72 | //
73 | // List of properties used in globalAccountSettings, globalNetworkCheckSettings
74 | // and globalNetworkAccountSettings variables (+ in the "auth" module):
75 | // - href
76 | // Depending on the setup type set the value to:
77 | // a.) globalAccountSettings: full "principal URL"
78 | // b.) globalNetworkCheckSettings: "principal URL" WITHOUT the "USER/" part
79 | // c.) globalNetworkAccountSettings: "full URL" to the "auth" directory
80 | // This property is supported in:
81 | // globalAccountSettings
82 | // globalNetworkCheckSettings
83 | // globalNetworkAccountSettings
84 | // - userAuth
85 | // - userName
86 | // Set the username you want to login.
87 | // - userPassword
88 | // Set the password for the given username.
89 | // This property is supported in:
90 | // globalAccountSettings
91 | // - timeOut
92 | // This option sets the timeout for jQuery .ajax call (in miliseconds).
93 | // Example:
94 | // timeOut: 90000
95 | // This property is supported in:
96 | // globalAccountSettings
97 | // globalNetworkCheckSettings
98 | // globalNetworkAccountSettings
99 | // - lockTimeOut
100 | // NOTE: used only if server supports LOCK requests
101 | // This option sets the LOCK timeout value if resource locking
102 | // is used (in miliseconds).
103 | // Example:
104 | // lockTimeOut: 10000
105 | // This property is supported in:
106 | // globalAccountSettings
107 | // globalNetworkCheckSettings
108 | // globalNetworkAccountSettings (available in auth module only)
109 | // - checkContentType
110 | // This option enables a content-type checking for server response.
111 | // If enabled then only objects with proper content-type are inserted
112 | // into the interface.
113 | // If you cannot see data in the interface you may try to disable it (useful
114 | // if your server returns wrong value in "propstat/prop/getcontenttype").
115 | // If undefined then content-type checking is enabled.
116 | // Examples:
117 | // checkContentType: true
118 | // checkContentType: false
119 | // This property is supported in:
120 | // globalAccountSettings
121 | // globalNetworkCheckSettings
122 | // globalNetworkAccountSettings (available in auth module only)
123 | // - settingsAccount
124 | // NOTE: server support for custom DAV properties is REQUIRED!
125 | // This option sets the account where the client properties such as:
126 | // loaded collections, enabled collections, ... are saved during
127 | // the logout and resource/collection synchronisation
128 | // NOTE: set it to true ONLY for ONE account!
129 | // Examples:
130 | // settingsAccount: true
131 | // settingsAccount: false
132 | // This property is supported in:
133 | // globalAccountSettings
134 | // globalNetworkCheckSettings
135 | // globalNetworkAccountSettings (available in auth module only)
136 | // - delegation
137 | // NOTE: server support for this functionality is REQUIRED!
138 | // This option allows you to load delegated (shared) collections.
139 | // If set to true (default) then delegation functionality is enabled,
140 | // and the interface allows you to load delegated collections.
141 | // If false then delegation functionality is completely disabled.
142 | // Examples:
143 | // delegation: true
144 | // delegation: false
145 | // This property is supported in:
146 | // globalAccountSettings
147 | // globalNetworkCheckSettings
148 | // globalNetworkAccountSettings (available in auth module only)
149 | // - additionalResources
150 | // This options sets the list of additional resources (e.g. shared resources
151 | // accessible by all users). If the server supports delegation (see
152 | // the delegation option above) there is no reason to use this option!
153 | // Supported values:
154 | // - array of URL encoded resource names (not collections), such as:
155 | // 'company'
156 | // 'shared_resource'
157 | // If empty (default) or undefined then shared resources are not loaded
158 | // using this option, but may be loaded using the delegation option.
159 | // Examples:
160 | // additionalResources=[]
161 | // additionalResources=['public', 'shared_resource']
162 | // This property is supported in:
163 | // globalNetworkCheckSettings
164 | // - hrefLabel
165 | // This option sets the server name in the resource header (useful if
166 | // you want to see custom resource header above the collections).
167 | // You can use the following variables in the value:
168 | // %H = full hostname (including the port number)
169 | // %h = full hostname (without the port number)
170 | // %D = full domain name
171 | // %d = only the first and second level domain
172 | // %P = principal name
173 | // %p = principal name without the @domain.com part (if present)
174 | // %U = logged user name
175 | // %u = logged user name without the @domain.com part (if present)
176 | // If undefined, empty or or null then '%d/%p [%u]' is used.
177 | // Examples:
178 | // hrefLabel: '%d/%p [%u]'
179 | // hrefLabel: '%D/%u'
180 | // This property is supported in:
181 | // globalAccountSettings
182 | // globalNetworkCheckSettings
183 | // globalNetworkAccountSettings (available in auth module only)
184 | // - forceReadOnly
185 | // This option sets the list of collections as "read-only".
186 | // Supported values:
187 | // - true
188 | // all collections will be "read-only"
189 | // - array of URL encoded
190 | // - collections, such as:
191 | // '/caldav.php/user/calendar/'
192 | // '/caldav.php/user%40domain.com/calendar/'
193 | // - regexes, such as:
194 | // new RegExp('^/caldav.php/user/calendar[0-9]/$', 'i')
195 | // specifies the list of collections marked as "read-only"
196 | // If null (default) or undefined then server detected privileges are used.
197 | // Examples:
198 | // forceReadOnly: null
199 | // forceReadOnly: true
200 | // forceReadOnly: ['/caldav.php/user/calendar/',
201 | // '/caldav.php/user/calendar2/']
202 | // forceReadOnly: [new RegExp('^/.*/user/calendar[0-9]/$', 'i')]
203 | // This property is supported in:
204 | // globalAccountSettings
205 | // globalNetworkCheckSettings
206 | // globalNetworkAccountSettings (available in auth module only, with
207 | // different syntax for regexes)
208 | // Special options not present in configuration examples:
209 | // NOTE: use ONLY if you know what are you doing!
210 | // - crossDomain
211 | // This option sets the crossDomain for jQuery .ajax call. If null (default)
212 | // then the value is autodetected /and the result is shown in the console/
213 | // - withCredentials
214 | // This option sets the withCredentials for jQuery .ajax call. The default
215 | // value is false and there is NO REASON to change it to true!
216 | // NOTE: if true, Access-Control-Allow-Origin "*" (CORS header) not works!
217 |
218 |
219 | // globalAccountSettings
220 | // Use this option if you want to use automatic login (without a login
221 | // screen) with hardcoded username/password in config.js. Otherwise use
222 | // globalNetworkCheckSettings or globalNetworkAccountSettings (see below).
223 | // NOTE: if this option is used the value must be an array of object(s).
224 | // List of properties used in globalAccountSettings variable:
225 | // - href
226 | // Set this option to the full "principal URL".
227 | // NOTE: the last character in the value must be '/'
228 | // - userAuth
229 | // - userName
230 | // Set the username you want to login.
231 | // - userPassword
232 | // Set the password for the given username.
233 | // NOTE: for description of other properties see comments at the beginning
234 | // of this file.
235 | // NOTE: for minimal/fast setup you need to set only the href and userAuth
236 | // options. It is safe/recommended to keep the remaining options unchanged!
237 | // Example:
238 | //var globalAccountSettings=[
239 | // {
240 | // href: 'https://server1.com:8443/caldav.php/USERNAME1/',
241 | // userAuth:
242 | // {
243 | // userName: 'USERNAME1',
244 | // userPassword: 'PASSWORD1'
245 | // },
246 | // timeOut: 90000,
247 | // lockTimeOut: 10000,
248 | // checkContentType: true,
249 | // settingsAccount: true,
250 | // delegation: true,
251 | // hrefLabel: null,
252 | // forceReadOnly: null
253 | // },
254 | // {
255 | // href: 'https://server2.com:8443/caldav.php/USERNAME2/',
256 | // ...
257 | // ...
258 | // }
259 | //];
260 | var globalAccountSettings=[
261 | {
262 | href: location.protocol+'//'+location.hostname+
263 | (location.port ? ':'+location.port: '')+'/radicale/owner/addressbook.vcf/',
264 | userAuth: {userName: 'someuser', userPassword: 'somepaswd'},
265 | timeOut: 90000,
266 | lockTimeOut: 10000,
267 | checkContentType: true,
268 | settingsAccount: false,
269 | delegation: true,
270 | hrefLabel: 'Current Grain',
271 | forceReadOnly: null,
272 | ignoreAlarms: false,
273 | backgroundCalendars: []
274 | }
275 | ];
276 | var globalEnabledApps = ['CardDavMATE'];
277 | // globalNetworkCheckSettings
278 | // Use this option if you want to use standard login screen without
279 | // hardcoded username/password in config.js (used by globalAccountSettings).
280 | // NOTE: if this option is used the value must be an object.
281 | // List of properties used in globalAccountSettings variable:
282 | // - href
283 | // Set this option to the "principal URL" WITHOUT the "USERNAME/"
284 | // part (this options uses the username from the login screen).
285 | // NOTE: the last character in the value must be '/'
286 | // NOTE: for description of other properties see comments at the beginning
287 | // of this file.
288 | // NOTE: for minimal/fast setup you need to set only the href option. It is
289 | // safe/recommended to keep the remaining options unchanged!
290 | // Example href values:
291 | // OS X server http example (see misc/readme_osx.txt for server setup):
292 | // href: 'http://osx.server.com:8008/principals/users/'
293 | // OS X server https example (see misc/readme_osx.txt for server setup):
294 | // href: 'https://osx.server.com:8443/principals/users/'
295 | // Cyrus server https example:
296 | // href: 'https://cyrus.server.com/dav/principals/user/'
297 | // Example:
298 | // Davical example which automatically detects the protocol, server name,
299 | // port, ... (client installed into Davical "htdocs" subdirectory;
300 | // works "out of the box", no additional setup required):
301 |
302 | // globalNetworkAccountSettings
303 | // Try this option ONLY if you have working setup using
304 | // globalNetworkCheckSettings and want to fix the authentication popup
305 | // window problem (if invalid username/password is entered)!
306 | // If you use this option then your browser sends username/password to the PHP
307 | // "auth" module ("auth" directory) instead of the DAV server itself.
308 | // The "auth" module then validates your username/password against your server,
309 | // and if the authentication is successful, then it sends back a configuration
310 | // XML (requires additional configuration). The resulting XML is handled
311 | // IDENTICALLY as the globalAccountSettings configuration option.
312 | // NOTE: for the "auth" module configuration see readme.txt!
313 | // NOTE: this option invokes a login screen and disallows access until
314 | // the client gets correct XML configuration file from the server!
315 | // List of properties used in globalNetworkAccountSettings variable:
316 | // - href
317 | // Set this option to the "full URL" of the "auth" directory
318 | // NOTE: the last character in the value must be '/'
319 | // NOTE: for description of other properties see comments at the beginning
320 | // of this file.
321 | // Example href values:
322 | // href: 'https://server.com/client/auth/'
323 | // Example:
324 | // Use this configuration if the "auth" module is located in the client
325 | // installation subdirectory (default):
326 | //var globalNetworkAccountSettings={
327 | // href: location.protocol+'//'+location.hostname+
328 | // (location.port ? ':'+location.port : '')+
329 | // location.pathname.replace(RegExp('index\.html$'),'')+
330 | // 'auth/',
331 | // timeOut: 30000
332 | //};
333 |
334 |
335 | // globalUseJqueryAuth
336 | // Use jQuery .ajax() auth or custom header for HTTP basic auth (default).
337 | // Set this option to true if your server uses digest auth (note: you may
338 | // experience auth popups on some browsers).
339 | // If undefined (or empty), custom header for HTTP basic auth is used.
340 | // Example:
341 | //var globalUseJqueryAuth=false;
342 |
343 |
344 | // globalBackgroundSync
345 | // Enable background synchronization even if the browser window/tab has no
346 | // focus.
347 | // If false, synchronization is performed only if the browser window/tab
348 | // is focused. If undefined or not false, then background sync is enabled.
349 | // Example:
350 | var globalBackgroundSync=true;
351 |
352 |
353 | // globalSyncResourcesInterval
354 | // This option defines how often (in miliseconds) are resources/collections
355 | // asynchronously synchronized.
356 | // Example:
357 | var globalSyncResourcesInterval=60000;
358 |
359 |
360 | // globalEnableRefresh
361 | // This option enables or disables the manual synchronization button in
362 | // the interface. If this option is enabled then users can perform server
363 | // synchronization manually. Enabling this option may cause high server
364 | // load (even DDOS) if users will try to manually synchronize data too
365 | // often (instead of waiting for the automatic synchronization).
366 | // If undefined or false, the synchronization button is disabled.
367 | // NOTE: enable this option only if you really know what are you doing!
368 | // Example:
369 | var globalEnableRefresh=true;
370 |
371 |
372 | // globalEnableKbNavigation
373 | // Enable basic keyboard navigation using arrow keys?
374 | // If undefined or not false, keyboard navigation is enabled.
375 | // Example:
376 | var globalEnableKbNavigation=true;
377 |
378 |
379 | // globalSettingsType
380 | // Where to store user settings such as: active view, enabled/selected
381 | // collections, ... (the client store them into DAV property on the server).
382 | // NOTE: not all servers support storing DAV properties (some servers support
383 | // only subset /or none/ of these URLs).
384 | // Supported values:
385 | // - 'principal-URL', '', null or undefined (default) => settings are stored
386 | // to principal-URL (recommended for most servers)
387 | // - 'addressbook-home-set' => settings are are stored to addressbook-home-set
388 | // Example:
389 | //var globalSettingsType='';
390 |
391 |
392 | // globalCrossServerSettingsURL
393 | // Settings such as enabled/selected collections are stored on the server
394 | // (see the previous option) in form of full URL
395 | // (e.g.: https://user@server:port/principal/collection/), but even if this
396 | // approach is "correct" (you can use the same principal URL with multiple
397 | // different logins, ...) it causes a problem if your server is accessible
398 | // from multiple URLs (e.g. http://server/ and https://server/). If you want
399 | // to store only the "principal/collection/" part of the URL (instead of the
400 | // full URL) then enable this option.
401 | // Example:
402 | //var globalCrossServerSettingsURL=false;
403 |
404 |
405 | // globalInterfaceLanguage
406 | // Default interface language (note: this option is case sensitive):
407 | // cs_CZ (Čeština [Czech])
408 | // da_DK (Dansk [Danish]; thanks Niels Bo Andersen)
409 | // de_DE (Deutsch [German]; thanks Marten Gajda and Thomas Scheel)
410 | // en_US (English [English/US])
411 | // es_ES (Español [Spanish]; thanks Damián Vila)
412 | // fr_FR (Français [French]; thanks John Fischer)
413 | // it_IT (Italiano [Italian]; thanks Luca Ferrario)
414 | // ja_JP (日本語 [Japan]; thanks Muimu Nakayama)
415 | // hu_HU (Magyar [Hungarian])
416 | // nl_NL (Nederlands [Dutch]; thanks Johan Vromans)
417 | // sk_SK (Slovenčina [Slovak])
418 | // tr_TR (Türkçe [Turkish]; thanks Selcuk Pultar)
419 | // ru_RU (Русский [Russian]; thanks Александр Симонов)
420 | // uk_UA (Українська [Ukrainian]; thanks Serge Yakimchuck)
421 | // zh_CN (中国 [Chinese]; thanks Fandy)
422 | // Example:
423 | var globalInterfaceLanguage='en_US';
424 |
425 |
426 | // globalInterfaceCustomLanguages
427 | // If defined and not empty then only languages listed here are shown
428 | // at the login screen, otherwise (default) all languages are shown
429 | // NOTE: values in the array must refer to an existing localization
430 | // (see the option above)
431 | // Example:
432 | // globalInterfaceCustomLanguages=['en_US', 'sk_SK'];
433 | var globalInterfaceCustomLanguages=[];
434 |
435 |
436 | // globalSortAlphabet
437 | // Use JavaScript localeCompare() or custom alphabet for data sorting.
438 | // Custom alphabet is used by default because JavaScript localeCompare()
439 | // not supports collation and often returns "wrong" result. If set to null
440 | // then localeCompare() is used.
441 | // Example:
442 | // var globalSortAlphabet=null;
443 | var globalSortAlphabet=' 0123456789'+
444 | 'AÀÁÂÄÆÃÅĀBCÇĆČDĎEÈÉÊËĒĖĘĚFGĞHIÌÍÎİÏĪĮJKLŁĹĽMNŃÑŇOÒÓÔÖŐŒØÕŌ'+
445 | 'PQRŔŘSŚŠȘșŞşẞTŤȚțŢţUÙÚÛÜŰŮŪVWXYÝŸZŹŻŽ'+
446 | 'aàáâäæãåābcçćčdďeèéêëēėęěfgğhiìíîïīįıjklłĺľmnńñňoòóôöőœøõō'+
447 | 'pqrŕřsśšßtťuùúûüűůūvwxyýÿzźżžАБВГҐДЕЄЖЗИІЇЙКЛМНОПРСТУФХЦЧШЩЮЯ'+
448 | 'Ьабвгґдеєжзиіїйклмнопрстуфхцчшщюяь';
449 |
450 |
451 | // globalSearchTransformAlphabet
452 | // To support search without diacritics (e.g. search for 'd' will find: 'Ď', 'ď')
453 | // it is required to define something like "character equivalence".
454 | // key = regex text, value = search character
455 | // Example:
456 | var globalSearchTransformAlphabet={
457 | '[ÀàÁáÂâÄ䯿ÃãÅåĀā]': 'a', '[ÇçĆćČč]': 'c', '[Ďď]': 'd',
458 | '[ÈèÉéÊêËëĒēĖėĘęĚě]': 'e', '[Ğğ]': 'g', '[ÌìÍíÎîİıÏïĪīĮį]': 'i',
459 | '[ŁłĹ弾]': 'l', '[ŃńÑñŇň]': 'n', '[ÒòÓóÔôÖöŐőŒœØøÕõŌō]': 'o',
460 | '[ŔŕŘř]': 'r', '[ŚśŠšȘșŞşẞß]': 's', '[ŤťȚțŢţ]': 't',
461 | '[ÙùÚúÛûÜüŰűŮůŪū]': 'u', '[ÝýŸÿ]': 'y', '[ŹźŻżŽž]': 'z'
462 | };
463 |
464 | // globalResourceAlphabetSorting
465 | // If more than one resource (server account) is configured, sort the
466 | // resources alphabetically?
467 | // Example:
468 | var globalResourceAlphabetSorting=true;
469 |
470 |
471 | // globalNewVersionNotifyUsers
472 | // Update notification will be shown only to users with login names defined
473 | // in this array.
474 | // If undefined (or empty), update notifications will be shown to all users.
475 | // Example:
476 | // globalNewVersionNotifyUsers=['admin', 'peter'];
477 | var globalNewVersionNotifyUsers=null;
478 |
479 |
480 | // globalDatepickerFormat
481 | // Set the datepicker format (see
482 | // http://docs.jquery.com/UI/Datepicker/formatDate for valid values).
483 | // NOTE: date format is predefined for each localization - use this option
484 | // ONLY if you want to use custom date format (instead of the localization
485 | // predefined one).
486 | // Example:
487 | //var globalDatepickerFormat='dd.mm.yy';
488 |
489 |
490 | // globalDatepickerFirstDayOfWeek
491 | // Set the datepicker first day of the week: Sunday is 0, Monday is 1, etc.
492 | // Example:
493 | var globalDatepickerFirstDayOfWeek=1;
494 |
495 |
496 | // globalHideInfoMessageAfter
497 | // How long are information messages (such as: success, error) displayed
498 | // (in miliseconds).
499 | // Example:
500 | var globalHideInfoMessageAfter=1800;
501 |
502 |
503 | // globalEditorFadeAnimation
504 | // Set the editor fade in/out animation duration when editing or saving data
505 | // (in miliseconds).
506 | // Example:
507 | var globalEditorFadeAnimation=300;
508 |
509 |
510 |
511 |
512 | // globalLoadedAddressbookCollections
513 | // This option sets the list of addressbook collections (down)loaded after
514 | // login. If empty then all addressbook collections for the currently logged
515 | // user are loaded.
516 | // NOTE: settings stored on the server (see settingsAccount) overwrite this
517 | // option.
518 | // Example:
519 | var globalLoadedAddressbookCollections=[];
520 |
521 |
522 | // globalActiveAddressbookCollections
523 | // This options sets the list of addressbook collections checked (enabled
524 | // checkbox => data visible in the interface) by default after login.
525 | // If empty then all loaded addressbook collections for the currently logged
526 | // user are checked.
527 | // NOTE: only already (down)loaded collections can be checked (see
528 | // the globalLoadedAddressbookCollections option).
529 | // NOTE: settings stored on the server (see settingsAccount) overwrite this
530 | // option.
531 | // Example:
532 | var globalActiveAddressbookCollections=[];
533 |
534 |
535 | // globalAddressbookSelected
536 | // This option sets which addressbook collection will be pre-selected
537 | // (if you create a new contact) by default after login.
538 | // The value must be URL encoded path to an addressbook collection,
539 | // for example: 'USER/addressbook/'
540 | // If empty or undefined then the first available addressbook collection
541 | // is selected automatically.
542 | // NOTE: only already (down)loaded collections can be pre-selected (see
543 | // the globalLoadedAddressbookCollections option).
544 | // NOTE: settings stored on the server (see settingsAccount) overwrite this
545 | // option.
546 | // Example:
547 | //var globalAddressbookSelected='';
548 |
549 |
550 | // globalCompatibility
551 | // This options is reserved for various compatibility settings.
552 | // NOTE: if this option is used the value must be an object.
553 | // Currently there is only one supported option:
554 | // - anniversaryOutputFormat
555 | // Different clients use different (and incompatible) approach
556 | // to store anniversary date in vCards. Apple stores this attribute as:
557 | // itemX.X-ABDATE;TYPE=pref:2000-01-01\r\n
558 | // itemX.X-ABLabel:_$!!$_\r\n'
559 | // other clients store this attribute as:
560 | // X-ANNIVERSARY:2000-01-01\r\n
561 | // Choose 'apple' or 'other' (lower case) for your 3rd party client
562 | // compatibility. You can chose both: ['apple', 'other'], but it may
563 | // cause many problems in the future, for example: duplicate anniversary
564 | // dates, invalid/old anniversary date in your clients, ...)
565 | // Examples:
566 | // anniversaryOutputFormat: ['other']
567 | // anniversaryOutputFormat: ['apple', 'other']
568 | // Example:
569 | var globalCompatibility={anniversaryOutputFormat: ['apple']};
570 |
571 |
572 | // globalUriHandler{Tel,Email,Url,Profile}
573 | // These options set the URI handlers for TEL, EMAIL, URL and X-SOCIALPROFILE
574 | // vCard attributes. Set them to null (or comment out) to disable.
575 | // NOTE: for globalUriHandlerTel is recommended to use 'tel:', 'callto:'
576 | // or 'skype:'. The globalUriHandlerUrl value is used only if no URI handler
577 | // is defined in the URL.
578 | // NOTE: it is safe to keep these values unchanged!
579 | // Example:
580 | var globalUriHandlerTel='tel:';
581 | var globalUriHandlerEmail='mailto:';
582 | var globalUriHandlerUrl='http://';
583 | var globalUriHandlerProfile={
584 | 'twitter': 'http://twitter.com/%u',
585 | 'facebook': 'http://www.facebook.com/%u',
586 | 'flickr': 'http://www.flickr.com/photos/%u',
587 | 'linkedin': 'http://www.linkedin.com/in/%u',
588 | 'myspace': 'http://www.myspace.com/%u',
589 | 'sinaweibo': 'http://weibo.com/n/%u'
590 | };
591 |
592 |
593 | // globalDefaultAddressCountry
594 | // This option sets the default country for new address fields.
595 | // See common.js or use the following command to get the list of
596 | // all supported country codes (defined in common.js):
597 | // grep -E "'[a-z]{2}':\s+\[" common.js | sed -Ee 's#^\s+|\s+\[\s+# #g'
598 | // Example:
599 | var globalDefaultAddressCountry='us';
600 |
601 |
602 | // globalAddressCountryEquivalence
603 | // This option sets the processing of the country field specified
604 | // in the vCard ADR attribute.
605 | // By default the address field in vCard looks like:
606 | // ADR;TYPE=WORK:;;1 Waters Edge;Baytown;LA;30314;USA\r\n
607 | // what cause a problem, because the country field is a plain
608 | // text and can contain any value, e.g.:
609 | // USA
610 | // United States of America
611 | // US
612 | // and because the address format can be completely different for
613 | // each country, e.g.:
614 | // China address example:
615 | // [China]
616 | // [Province] [City]
617 | // [Street]
618 | // [Postal]
619 | // Japan address example:
620 | // [Postal]
621 | // [Prefecture] [County/City]
622 | // [Further Divisions]
623 | // [Japan]
624 | // the client needs to correctly detect the country from the ADR
625 | // attribute. Apple solved this problem by using:
626 | // item1.ADR;TYPE=WORK:;;1 Waters Edge;Baytown;LA;30314;USA\r\n
627 | // item1.X-ABADR:us\r\n
628 | // where the second "related" attribute defines the country code
629 | // for the ADR attribute. This client uses the same approach, but
630 | // if the vCard is created by 3rd party clients and the X-ABADR
631 | // is missing, it is possible to define additional "rules" for
632 | // country matching. These rules are specied by the country code
633 | // (for full list of country codes see the comment for pre previous
634 | // option) and a case insensitive regular expression (which matches
635 | // the plain text value in the country field).
636 | // NOTE: if X-ABADR is not present and the country not matches any
637 | // country defined in this option, then globalDefaultAddressCountry
638 | // is used by default.
639 | // Example:
640 | var globalAddressCountryEquivalence=[
641 | {country: 'de', regex: '^\\W*Deutschland\\W*$'},
642 | {country: 'sk', regex: '^\\W*Slovensko\\W*$'}
643 | ];
644 |
645 |
646 | // globalAddressCountryFavorites
647 | // This option defines the list of countries which are shown at the top
648 | // of the country list in the interface (for full list of country codes
649 | // see the comment for pre globalDefaultAddressCountry option).
650 | // Example:
651 | // var globalAddressCountryFavorites=['de','sk'];
652 | var globalAddressCountryFavorites=[];
653 |
654 |
655 | // globalAddrColorPropertyXmlns
656 | // This options sets the namespace used for storing the "addressbook-color"
657 | // property by the client.
658 | // If true, undefined (or empty) "http://inf-it.com/ns/ab/" is used.
659 | // If false, then the addressbook color modification functionality
660 | // is completely disabled, and addressbook colors in the interface are
661 | // generated automatically.
662 | // Example:
663 | //var globalAddrColorPropertyXmlns=true;
664 |
665 |
666 | // globalContactStoreFN
667 | // This option specifies how the FN (formatted name) is stored into vCard.
668 | // The value for this options must be an array of strings, that can contain
669 | // the following variables:
670 | // prefix
671 | // last
672 | // middle
673 | // first
674 | // suffix
675 | // The string element of the array can contain any other characters (usually
676 | // space or colon). Elements are added into FN only if the there is
677 | // a variable match, for example if:
678 | // last='Lastname'
679 | // first='Firstname'
680 | // middle='' (empty)
681 | // and this option is set to:
682 | // ['last', ' middle', ' first'] (space in the second and third element)
683 | // the resulting value for FN will be: 'Lastname Firstname' and not
684 | // 'Lastname Firstname' (two spaces), because the middle name is empty (so
685 | // the second element is completely ignored /not added into FN/).
686 | // NOTE: this attribute is NOT used by this client, and it is also NOT
687 | // possible to directly edit it in the interface.
688 | // Examples:
689 | // var globalContactStoreFN=[' last', ' middle', ' first'];
690 | // var globalContactStoreFN=['last', ', middle', ' ,first'];
691 | var globalContactStoreFN=['prefix',' last',' middle',' first',' suffix'];
692 |
693 |
694 | // globalGroupContactsByCompanies
695 | // This options specifies how contacts are grouped in the interface.
696 | // By default the interface looks like (very simple example):
697 | // A
698 | // Adams Adam
699 | // Anderson Peter
700 | // B
701 | // Brown John
702 | // Baker Josh
703 | // if grouped by company/deparment the result is:
704 | // Company A [Department X]
705 | // Adams Adam
706 | // Brown John
707 | // Company B [Department Y]
708 | // Anderson Peter
709 | // Baker Josh
710 | // If this option is set to true contacts are grouped by company/department,
711 | // otherwise (default) contacts are grouped by letters of the alphabet.
712 | // If undefined or not true, grouping by alphabet letters is used.
713 | // NOTE: see also the globalCollectionDisplay option below.
714 | var globalGroupContactsByCompanies=false;
715 |
716 |
717 | // globalCollectionDisplay
718 | // This options specifies how data columns in the contact list are displayed.
719 | //
720 | // NOTE: columns are displayed ONLY if there is enought horizontal place in
721 | // the browser window (e.g. if you define 5 columns here, but your browser
722 | // window is not wide enough, you will see only first 3 columns instead of 5).
723 | //
724 | // NOTE: see the globalContactDataMinVisiblePercentage option which defines the
725 | // width for columns.
726 | //
727 | // The value must be an array of columns, where each column is represented by
728 | // an object with the following properties:
729 | // label => the value of this option is a string used as column header
730 | // You can use the following localized variables in the label string:
731 | // - {Name}
732 | // - {FirstName}
733 | // - {LastName}
734 | // - {MiddleName}
735 | // - {NickName}
736 | // - {Prefix}
737 | // - {Suffix}
738 | // - {BirthDay}
739 | // - {PhoneticLastName}
740 | // - {PhoneticFirstName}
741 | // - {JobTitle}
742 | // - {Company}
743 | // - {Department}
744 | // - {Categories}
745 | // - {NoteText}
746 | // - {Address}, {AddressWork}, {AddressHome}, {AddressOther}
747 | // - {Phone}, {PhoneWork}, {PhoneHome}, {PhoneCell}, {PhoneMain},
748 | // {PhonePager}, {PhoneFax}, {PhoneIphone}, {PhoneOther}
749 | // - {Email}, {EmailWork}, {EmailHome}, {EmailMobileme}, {EmailOther}
750 | // - {URL}, {URLWork}, {URLHome}, {URLHomepage}, {URLOther}
751 | // - {Dates}, {DatesAnniversary}, {DatesOther}
752 | // - {Related}, {RelatedManager}, {RelatedAssistant}, {RelatedFather},
753 | // {RelatedMother}, {RelatedParent}, {RelatedBrother}, {RelatedSister},
754 | // {RelatedChild}, {RelatedFriend}, {RelatedSpouse}, {RelatedPartner},
755 | // {RelatedOther}
756 | // - {Profile}, {ProfileTwitter}, {ProfileFacebook}, {ProfileFlickr},
757 | // {ProfileLinkedin}, {ProfileMyspace}, {ProfileSinaweibo}
758 | // - {IM}, {IMWork}, {IMHome}, {IMMobileme}, {IMOther}, {IMAim}, {IMIcq},
759 | // {IMIrc}, {IMJabber}, {IMMsn}, {IMYahoo}, {IMFacebook}, {IMGadugadu},
760 | // {IMGoogletalk}, {IMQq}, {IMSkype}
761 | // value => the value of this option is an array of format strings, or
762 | // an object with the following properties:
763 | // - company (used for company contacts)
764 | // - personal (used for user contacts)
765 | // where the value of these properties is an array of format strings used
766 | // for company or user contacts (you can have different values in the same
767 | // column for personal and company contacts).
768 | // You can use the following simple variables in the format string:
769 | // - {FirstName}
770 | // - {LastName}
771 | // - {MiddleName}
772 | // - {NickName}
773 | // - {Prefix}
774 | // - {Suffix}
775 | // - {BirthDay}
776 | // - {PhoneticLastName}
777 | // - {PhoneticFirstName}
778 | // - {JobTitle}
779 | // - {Company}
780 | // - {Department}
781 | // - {Categories}
782 | // - {NoteText}
783 | // You can also use parametrized variables, where the parameter is enclosed
784 | // in square bracket. Paramatrized variables are useful to extract data
785 | // such as home phone {Phone[type=home]}, extract the second phone number
786 | // {Phone[:1]} (zero based indexing) or extract the third home phone number
787 | // {Phone[type=home][:2]} from the vCard.
788 | // NOTE: if the parametrized variable matches multiple items, e.g.:
789 | // {Phone[type=work]} (if the contact has multiple work phones) then the
790 | // first one is used!
791 | //
792 | // The following parametrized variables are supported (note: you can use
793 | // all of them also without parameters /the first one will be used/):
794 | // - {Address[type=XXX]} or {Address[:NUM]} or {Address[type=XXX][:NUM]}
795 | // where supported values for XXX are:
796 | // - work
797 | // - home
798 | // - other
799 | // - any other custom value
800 | // - {Phone[type=XXX]} or {Phone[:NUM]} or {Phone[type=XXX][:NUM]}
801 | // where supported values for XXX are:
802 | // - work
803 | // - home
804 | // - cell
805 | // - main
806 | // - pager
807 | // - fax
808 | // - iphone
809 | // - other
810 | // - any other custom value
811 | // - {Email[type=XXX]} or {Email[:NUM]} or {Email[type=XXX][:NUM]}
812 | // where supported values for XXX are:
813 | // - work
814 | // - home
815 | // - mobileme
816 | // - other
817 | // - any other custom value
818 | // - {URL[type=XXX]} or {URL[:NUM]} or {URL[type=XXX][:NUM]}
819 | // where supported values for XXX are:
820 | // - work
821 | // - home
822 | // - homepage
823 | // - other
824 | // - any other custom value
825 | // - {Dates[type=XXX]} or {Dates[:NUM]} or {Dates[type=XXX][:NUM]}
826 | // where supported values for XXX are:
827 | // - anniversary
828 | // - other
829 | // - any other custom value
830 | // - {Related[type=XXX]} or {Related[:NUM]} or {Related[type=XXX][:NUM]}
831 | // where supported values for XXX are:
832 | // - manager
833 | // - assistant
834 | // - father
835 | // - mother
836 | // - parent
837 | // - brother
838 | // - sister
839 | // - child
840 | // - friend
841 | // - spouse
842 | // - partner
843 | // - other
844 | // - any other custom value
845 | // - {Profile[type=XXX]} or {Profile[:NUM]} or {Profile[type=XXX][:NUM]}
846 | // where supported values for XXX are:
847 | // - twitter
848 | // - facebook
849 | // - flickr
850 | // - linkedin
851 | // - myspace
852 | // - sinaweibo
853 | // - any other custom value
854 | // - {IM[type=XXX]} or {IM[service-type=YYY]} or {IM[:NUM]}
855 | // where supported values for XXX are:
856 | // - work
857 | // - home
858 | // - mobileme
859 | // - other
860 | // - any other custom value
861 | // and supported values for YYY are:
862 | // - aim
863 | // - icq
864 | // - irc
865 | // - jabber
866 | // - msn
867 | // - yahoo
868 | // - facebook
869 | // - gadugadu
870 | // - googletalk
871 | // - qq
872 | // - skype
873 | // - any other custom value
874 | //
875 | // NOTE: if you want to use the "any other custom value" option (for XXX
876 | // or YYY above) you MUST double escape the following characters:
877 | // =[]{}\
878 | // for example:
879 | // - for profile type "=XXX=" use: '{Profile[type=\\=XXX\\=]}'
880 | // - for profile type "\XXX\" use: '{Profile[type=\\\\XXX\\\\]}'
881 | //
882 | // NOTE: if you want to use curly brackets in the format string you must
883 | // double escape it, e.g.: ['{Company}', '\\{{Department}\\}']
884 | //
885 | // The format string (for the value option) is an array to allow full
886 | // customization of the interface. For example if:
887 | // value: ['{LastName} {MiddleName} {FirstName}']
888 | // and the person has no middle name, then the result in the column
889 | // will be (without quotes):
890 | // "Parker Peter" (note: two space characters)
891 | // but if you use:
892 | // value: ['{LastName}', ' {MiddleName}', ' {FirstName}']
893 | // then the result will be (without quotes):
894 | // "Parker Peter" (note: only one space character)
895 | // The reason is that only those elements of the array are appended
896 | // into the result where non-empty substitution was performed (so the
897 | // ' {MiddleName}' element in this case is ignored, because the person
898 | // in the example above has no /more precisely has empty/ middle name).
899 | //
900 | // Examples:
901 | // To specify two columns (named "Company" and "Department / LastName"),
902 | // where the first will display the company name, and the second will display
903 | // department for company contacts (with "Dep -" prefix), and lastname for
904 | // personal contacts (with "Name -" prefix) use:
905 | // var globalCollectionDisplay=[
906 | // {
907 | // label: 'Company',
908 | // value: ['{Company}']
909 | // },
910 | // {
911 | // label: 'Department / LastName',
912 | // value: {
913 | // company: ['Dep - {Department}'],
914 | // personal: ['Name - {LastName}']
915 | // }
916 | // }
917 | // ];
918 | // To specify 3 columns (named "Categories", "URL" and "IM"), where the first
919 | // will display categories, second will display the third work URL, and third
920 | // will display ICQ IM use:
921 | // var globalCollectionDisplay=[
922 | // {
923 | // label: 'Categories',
924 | // value: ['{Categories}']
925 | // },
926 | // {
927 | // label: 'URL',
928 | // value: ['{URL[type=WORK][:2]}']
929 | // },
930 | // {
931 | // label: 'IM',
932 | // value: ['{IM[service-type=ICQ]}']
933 | // }
934 | // ];
935 | //
936 | // Recommended settings if globalGroupContactsByCompanies
937 | // is set to false:
938 | // var globalCollectionDisplay=[
939 | // {
940 | // label: '{Name}',
941 | // value: ['{LastName}', ' {MiddleName}', ' {FirstName}']
942 | // },
943 | // {
944 | // label: '{Company} [{Department}]',
945 | // value: ['{Company}', ' [{Department}]']
946 | // },
947 | // {
948 | // label: '{JobTitle}',
949 | // value: ['{JobTitle}']
950 | // },
951 | // {
952 | // label: '{Email}',
953 | // value: ['{Email[:0]}']
954 | // },
955 | // {
956 | // label: '{Phone} 1',
957 | // value: ['{Phone[:0]}']
958 | // },
959 | // {
960 | // label: '{Phone} 2',
961 | // value: ['{Phone[:1]}']
962 | // },
963 | // {
964 | // label: '{NoteText}',
965 | // value: ['{NoteText}']
966 | // }
967 | // ];
968 | //
969 | // Recommended settings if globalGroupContactsByCompanies
970 | // is set to true:
971 | // var globalCollectionDisplay=[
972 | // {
973 | // label: '{Name}',
974 | // value: {
975 | // personal: ['{LastName}', ' {MiddleName}', ' {FirstName}'],
976 | // company: ['{Company}', ' [{Department}]']
977 | // }
978 | // },
979 | // {
980 | // label: '{JobTitle}',
981 | // value: ['{JobTitle}']
982 | // },
983 | // {
984 | // label: '{Email}',
985 | // value: ['{Email[:0]}']
986 | // },
987 | // {
988 | // label: '{Phone} 1',
989 | // value: ['{Phone[:0]}']
990 | // },
991 | // {
992 | // label: '{Phone} 2',
993 | // value: ['{Phone[:1]}']
994 | // },
995 | // {
996 | // label: '{NoteText}',
997 | // value: ['{NoteText}']
998 | // }
999 | // ];
1000 | //
1001 | // NOTE: if left undefined, the recommended settings will be used.
1002 |
1003 |
1004 | // globalCollectionSort
1005 | // This options sets the ordering of contacts in the interface. In general
1006 | // contacts are ordered alphabetically by an internal "sort string" which
1007 | // is created for each contact. Here you can specify how this internal string
1008 | // is created. The value is a simple array holding only the values from the
1009 | // value property defined in the globalCollectionDisplay option.
1010 | // If undefined, the definition from globalCollectionDisplay is used.
1011 | // Example:
1012 | // var globalCollectionSort = [
1013 | // ['{LastName}'],
1014 | // ['{FirstName}'],
1015 | // ['{MiddleName}'],
1016 | // {
1017 | // company: ['{Categories}'],
1018 | // personal: ['{Company}']
1019 | // }
1020 | // ];
1021 |
1022 |
1023 | // globalContactDataMinVisiblePercentage
1024 | // This option defines how the width for columns are computed. If you set
1025 | // it to 1 then 100% of all data in the column will be visible (the column
1026 | // width is determined by the longest string in the column). If you set it
1027 | // to 0.95 then 95% of data will fit into the column width, and the remaining
1028 | // 5% will be truncated (" ...").
1029 | // Example:
1030 | var globalContactDataMinVisiblePercentage=0.95;
1031 |
1032 |
1033 |
--------------------------------------------------------------------------------
/infcloud/addressbook.js:
--------------------------------------------------------------------------------
1 | /*
2 | InfCloud - the open source CalDAV/CardDAV Web Client
3 | Copyright (C) 2011-2015
4 | Jan Mate
5 | Andrej Lezo
6 | Matej Mihalik
7 |
8 | This program is free software: you can redistribute it and/or modify
9 | it under the terms of the GNU Affero General Public License as
10 | published by the Free Software Foundation, either version 3 of the
11 | License, or (at your option) any later version.
12 |
13 | This program is distributed in the hope that it will be useful,
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 | GNU Affero General Public License for more details.
17 |
18 | You should have received a copy of the GNU Affero General Public License
19 | along with this program. If not, see .
20 | */
21 |
22 | // AddressbookList Class
23 | function AddressbookList()
24 | {
25 | this.contacts=new Array();
26 | this.contacts_hash=new Object();
27 | this.contacts_hash_uidattr=new Object();
28 | this.companies=new Array();
29 | this.companies_hash=new Object();
30 | this.companies_hash_uidattr=new Object();
31 | this.vcard_groups=new Object();
32 | this.contact_categories=new Object();
33 | this.contact_companies=new Object();
34 | this.contactLoaded=null;
35 | this.contactToReload=null;
36 | this.vcardGroupLoaded=null;
37 |
38 | this.reset=function()
39 | {
40 | this.contacts.splice(0,this.contacts.length);
41 | this.contacts_hash=new Object();
42 | this.contacts_hash_uidattr=new Object();
43 | this.companies.splice(0,this.companies.length);
44 | this.companies_hash=new Object();
45 | this.companies_hash_uidattr=new Object();
46 | this.vcard_groups=new Object(); // these are not removed from the interface (it's OK)
47 | this.contact_categories=new Object();
48 | this.contact_companies=new Object();
49 | this.contactLoaded=null;
50 | this.contactToReload=null;
51 | this.vcardGroupLoaded=null;
52 | };
53 |
54 | this.getNewUID=function()
55 | {
56 | // we count with uniqueness of generated hash string
57 | var newUID=null;
58 | newUID=generateUID();
59 | return newUID;
60 | };
61 |
62 | this.getLoadedContactUID=function()
63 | {
64 | if(this.contactLoaded!=null)
65 | return this.contactLoaded.uid;
66 | else
67 | return '';
68 | };
69 |
70 | this.getSortKey=function(inputContact, inputSettings, inputMode) // inputMode (0=sort, 1=display)
71 | {
72 | var vcard_element=('\r\n'+inputContact.vcard).match(vCard.pre['contentline_N']);
73 | if(vcard_element===null || vcard_element.length!==1) // if the N attribute is not present exactly once, vCard is considered invalid
74 | return false;
75 |
76 | var sortKeyCompanyPart='';
77 | if(typeof (getCRMSortKey)== 'function' && inputMode==0)
78 | {
79 | sortKeyCompanyPart=getCRMSortKey(inputContact);
80 | if(inputContact.isCompany!=undefined && inputContact.isCompany)
81 | return sortKeyCompanyPart; // for company contact we can return here
82 | }
83 | else if(typeof globalGroupContactsByCompanies!='undefined' && globalGroupContactsByCompanies==true)
84 | {
85 | var sortKeyCompanyPart='\u0009';
86 | var vcard_orgname=('\r\n'+inputContact.vcard).match(vCard.pre['contentline_ORG']);
87 | if(vcard_orgname!=null && vcard_orgname.length>0) // if more than one ORG is present, use the first one
88 | {
89 | // parsed (contentline_parse) = [1]->"group.", [2]->"name", [3]->";param;param", [4]->"value"
90 | var parsed=vcard_orgname[0].match(vCard.pre['contentline_parse']);
91 | var parsed_value=vcardSplitValue(parsed[4], ';');
92 |
93 | sortKeyCompanyPart=parsed_value[0]+'\u0009'+(parsed_value[1]!=undefined ? parsed_value[1] : '')+'\u0009';
94 |
95 | if(inputMode==0 && inputContact.isCompany!=undefined && inputContact.isCompany)
96 | return sortKeyCompanyPart; // for company contact we can return here
97 | }
98 | }
99 |
100 | var tmp = [];
101 | var isGroup = this.isContactGroup(inputContact.vcard);
102 | /* backward compatibility for stupid users (remove it in future) */
103 | if(typeof inputSettings==='string')
104 | tmp = inputSettings.replace(RegExp(',','g'), ', ').split(',');
105 | else if($.isArray(inputSettings)) /* new configuration options (arrays) */
106 | tmp = inputSettings.slice(); // copy the configuration array
107 |
108 | // display settings for non-group contacts need some flattening
109 | if(inputMode===1 && !isGroup) {
110 | tmp = $.map(tmp, function(el) {
111 | if($.isPlainObject(el.value)) {
112 | return el.value;
113 | }
114 | else {
115 | return [el.value];
116 | }
117 |
118 | });
119 | }
120 |
121 | // now flatten the array completely to a company / personal version
122 | tmp = $.map(tmp, function(el) {
123 | if($.isPlainObject(el)) {
124 | if(inputContact.isCompany && el.hasOwnProperty('company')) {
125 | return [el.company];
126 | }
127 | else if(!inputContact.isCompany && el.hasOwnProperty('personal')) {
128 | return [el.personal];
129 | }
130 |
131 | return [];
132 | }
133 |
134 | return [el];
135 | });
136 |
137 | for(var i=0; i"group.", [2]->"name", [3]->";param;param", [4]->"value"
149 | var parsed=vcard_element2[0].match(vCard.pre['contentline_parse']);
150 | var sort_value=parsed[4];
151 | }
152 | }
153 |
154 | return (inputMode===0 ? sortKeyCompanyPart+sort_value : sort_value);
155 | };
156 |
157 | this.isContactGroup=function(inputVcard)
158 | {
159 | var vcard_element=null;
160 | if((vcard_element=('\r\n'+inputVcard).match(vCard.pre['X-ADDRESSBOOKSERVER-KIND']))!=null)
161 | {
162 | // parsed (contentline_parse) = [1]->"group.", [2]->"name", [3]->";param;param", [4]->"value"
163 | var parsed=vcard_element[0].match(vCard.pre['contentline_parse']);
164 | if(parsed[4].toLowerCase()=='group')
165 | return true;
166 | }
167 | return false;
168 | };
169 |
170 | this.getMyContactGroups=function(inputUid)
171 | {
172 | if(this.contacts_hash[inputUid]!=undefined)
173 | {
174 | var myContactGroups=new Array();
175 |
176 | if((vcard_element=this.contacts_hash[inputUid].vcard.match(vCard.pre['contentline_UID']))!=null)
177 | {
178 | // parsed (contentline_parse) = [1]->"group.", [2]->"name", [3]->";param;param", [4]->"value"
179 | parsed=vcard_element[0].match(vCard.pre['contentline_parse']);
180 |
181 | for(var j=0;j"group.", [2]->"name", [3]->";param;param", [4]->"value"
202 | parsed=vcard_element[0].match(vCard.pre['contentline_parse']);
203 |
204 | for(var j=0;j"group.", [2]->"name", [3]->";param;param", [4]->"value"
251 | parsed=vcard_element[0].match(vCard.pre['contentline_parse']);
252 |
253 | var changedContactGroups=new Array();
254 |
255 | for(var j=0;j0) return false; else globalAddressbookList.applyABFilter(groupChBoxClick(this, \'#ResourceCardDAVList\', \'.resourceCardDAV_header\', \'.resourceCardDAV\', \'.contact_group\', true), false);'});
350 |
351 | newElement.append(vcardUnescapeValue(inputContact.displayvalue));
352 | newElement.css('display','');
353 | if($('#ResourceCardDAVList').find('[data-id="'+jqueryEscapeSelector(inputContact.uid.replace(RegExp('/[^/]*$',''),'/'))+'"]').next('.contact_group').find('[data-id="'+jqueryEscapeSelector(inputContact.uid)+'"]').length==0)
354 | $('#ResourceCardDAVList').find('[data-id="'+jqueryEscapeSelector(inputContact.uid.replace(RegExp('/[^/]*$',''),'/'))+'"]').next('.contact_group').children().eq(insertIndex).after(newElement);
355 |
356 | // make the area droppable if the collection is not read-only
357 | if(globalResourceCardDAVList.getCollectionPrivByUID(inputContact.uid.replace(RegExp('[^/]*$',''),''))==false && (typeof globalDisableDragAndDrop=='undefined' || globalDisableDragAndDrop!=true))
358 | $('#ResourceCardDAVList').find('[data-id="'+jqueryEscapeSelector(inputContact.uid.replace(RegExp('[^/]*$',''),''))+'"]').parent().find('.contact_group').children().eq(insertIndex+1).droppable({
359 | accept: '.ablist_item',
360 | tolerance: 'pointer',
361 | hoverClass: 'group_dropped_to',
362 | drop: function(event, ui){
363 | // animate the clone of the dropped (draggable) element
364 | var tmp=ui.helper.clone();
365 | tmp.appendTo('body')
366 | .animate({opacity: 0, color: 'transparent', height: 0, width: 0, fontSize: 0, lineHeight: 0, paddingLeft: 0, paddingRight: 0},750,function(){tmp.remove()});
367 |
368 | // disallow to drag the original dropped element until the processing is finished
369 | ui.draggable.draggable('option', 'disabled', true);
370 |
371 | // animate the original dropped element
372 | ui.draggable.animate({opacity: 0.3}, 750);
373 |
374 | // disallow to drop any new element until the processing is finished
375 | $(this).droppable('option', 'disabled', true);
376 |
377 | // show the loader icon
378 | $(this).addClass('r_operate');
379 |
380 | var tmp2=globalAddressbookList.getContactByUID(ui.draggable.attr('data-id'));
381 | tmp2.addToContactGroupUID='';
382 | tmp2.removeToContactGroupUID=new Array();
383 | tmp2.addToContactGroupUID=$(this).attr('data-id');
384 | tmp2.uiObjects={contact: ui.draggable, resource: $(this).attr('data-id')};
385 |
386 | lockAndPerformToCollection(tmp2, globalRefAddContact.attr('data-filter-url'), 'ADD_TO_GROUP');
387 | }
388 | });
389 |
390 | // if no new makeActive but forceReload is true then reload the current contact group
391 | if(makeActive==null && forceReload==true)
392 | makeActive=globalRefAddContact.attr('data-filter-url');
393 |
394 | // load the contact group if it was selected
395 | if(makeActive!=null)
396 | {
397 | $('#ResourceCardDAVList').find('.resourceCardDAV_item').find('.resourceCardDAV_selected').removeClass('resourceCardDAV_selected');
398 | $('#ResourceCardDAVList').find('[data-id='+jqueryEscapeSelector(makeActive.replace(RegExp('[^/]*$',''),''))+']').addClass('resourceCardDAV_selected');
399 | $('#ResourceCardDAVList').find('[data-id='+jqueryEscapeSelector(makeActive)+']').addClass('resourceCardDAV_selected');
400 | }
401 | if(makeChecked!=null)
402 | {
403 | $('#ResourceCardDAVList').find('[data-id='+jqueryEscapeSelector(makeChecked)+']').find('input[type=checkbox]').prop('checked',true);
404 | this.applyABFilter(dataGetChecked('#ResourceCardDAVList'), false);
405 | }
406 | };
407 |
408 | this.removeContactGroup=function(inputUid, loadNext)
409 | {
410 | for(var i=this.vcard_groups[inputUid.replace(RegExp('/[^/]*$',''),'/')].length-1;i>=0;i--)
411 | if(this.vcard_groups[inputUid.replace(RegExp('/[^/]*$',''),'/')][i].uid==inputUid)
412 | {
413 | var uidRemoved=this.vcard_groups[inputUid.replace(RegExp('/[^/]*$',''),'/')][i].uid;
414 | var item=$('#ResourceCardDAVList').find('[data-id^="'+jqueryEscapeSelector(this.vcard_groups[inputUid.replace(RegExp('/[^/]*$',''),'/')][i].uid)+'"]');
415 |
416 | // remove the item
417 | item.remove();
418 | this.vcard_groups[inputUid.replace(RegExp('/[^/]*$',''),'/')].splice(i,1);
419 |
420 | // vcardGroupLoaded bolo zrusene, pozriet co s tym
421 | if(loadNext && this.vcardGroupLoaded!=null && this.vcardGroupLoaded.uid==inputUid)
422 | {
423 | this.vcardGroupLoaded=null;
424 |
425 | // set the whole collection as active
426 | var tmp=uidRemoved.match(RegExp('(^.*/)'),'');
427 | // XXX it is no longer needed
428 | // globalResourceCardDAVList.loadAddressbookByUID(tmp[1]);
429 | }
430 | break;
431 | }
432 | };
433 |
434 | // hide/show contacts in the interface according to contactGroupOrResourceUid or search filter in the interface (contactGroupOrResourceUid==false)
435 | this.applyABFilter=function(contactGroupOrResourceUid, inputForceLoadNext)
436 | {
437 | if(globalCardDAVInitLoad)
438 | return false;
439 |
440 | // XXX docasne, potom dame prec
441 | if(!(contactGroupOrResourceUid instanceof Array))
442 | return false;
443 |
444 | var vcardGroupOrCollection=[];
445 | for(var i=0;i"group.", [2]->"name", [3]->";param;param", [4]->"value"
500 | parsed=vcard_element[0].match(vCard.pre['contentline_parse']);
501 | vcardUIDList[vcardUIDList.length]=parsed[4].replace('urn:uuid:','');
502 | // remove the processed parameter
503 | vcard=vcard.replace(vcard_element[0],'\r\n');
504 | }
505 |
506 | // update the contacts' "show" attribute
507 | for(var j=0;j"group.", [2]->"name", [3]->";param;param", [4]->"value"
516 | parsed=vcard_element[0].match(vCard.pre['contentline_parse']);
517 |
518 | if(vcardUIDList[j]==parsed[4] && this.contacts[k].search_hide==false)
519 | {
520 | this.contacts[k].show=true;
521 | this.contacts_hash[this.contacts[k].uid].show=true
522 | }
523 | }
524 | }
525 | }
526 | }
527 |
528 | var lastActive=null;
529 | var prevHeader=null;
530 | var lastContactForHeader=this.contacts.length-1;
531 | // performance
532 | var tmpListRefChildren=globalRefABListTable.children();
533 | // init displayed columns text length cache
534 | var columnLengths = [];
535 | for(var i=0; i=0;i--)
541 | {
542 | if(this.contacts[i].headerOnly==undefined)
543 | {
544 | // find the previous header index
545 | for(var j=i-1;j>=0;j--)
546 | if(this.contacts[j].headerOnly!=undefined && this.contacts[j].headerOnly==true)
547 | {
548 | prevHeader=j;
549 | break;
550 | }
551 |
552 | // performance
553 | var tmpListRefChildren_i=tmpListRefChildren.eq(i);
554 | var tmpListRefChildren_prev=tmpListRefChildren.eq(prevHeader);
555 |
556 | var coll_tmp=this.contacts[i].uid.match(RegExp('^(https?://)([^@/]+(?:@[^@/]+)?)@([^/]+)(.*/)([^/]+/)([^/]*)','i'));
557 | var collection_uid=coll_tmp[1]+coll_tmp[2]+'@'+coll_tmp[3]+coll_tmp[4]+coll_tmp[5];
558 | var coll_color=globalResourceCardDAVList.getCollectionByUID(collection_uid).color;
559 | this.contacts[i].color = coll_color;
560 | tmpListRefChildren_i.find('.ablist_item_color').css('background-color', coll_color);
561 | switch(this.contacts[i].show)
562 | {
563 | case false:
564 | tmpListRefChildren_i.css('display','none');
565 | if(tmpListRefChildren_i.hasClass('ablist_item_selected'))
566 | lastActive=i;
567 |
568 | var hideHeader=true;
569 | for(j=prevHeader+1;j<=lastContactForHeader;j++)
570 | if(this.contacts[j].show==true)
571 | {
572 | hideHeader=false;
573 | break;
574 | }
575 |
576 | if(hideHeader)
577 | tmpListRefChildren_prev.css('display','none');
578 |
579 | break;
580 | case true:
581 | // set the contact header to visible
582 | tmpListRefChildren_prev.css('display','');
583 |
584 | // set the contact to visible
585 | tmpListRefChildren_i.css('display','');
586 |
587 | // save column text length into cache
588 | tmpListRefChildren_i.children().slice(globalFixedContactDataColumnsCount).each(function(ind) {
589 | columnLengths[ind].push($(this).text().length);
590 | });
591 |
592 | break;
593 | default:
594 | break;
595 | }
596 | }
597 | else
598 | lastContactForHeader=i-1;
599 | }
600 |
601 | setDataColumnsWidth(columnLengths);
602 |
603 | // the previously loaded contact is hidden or not exists we need to select a new one
604 | if(inputForceLoadNext==true || $('#vCardEditor').attr('data-editor-state')!='edit' && (lastActive!=null || globalRefABListTable.children('.ablist_item_selected').length==0))
605 | {
606 | var nextCandidateToLoad=null;
607 | // get the nearest candidate to load
608 | // if we can go forward
609 | if(this.contactToReload!=null)
610 | nextCandidateToLoad=this.contactToReload;
611 | else
612 | {
613 | for(j=(previousActiveIndex==null ? 0 : previousActiveIndex);j=0;j--)
623 | if((this.contacts[j].headerOnly==undefined || this.contacts[j].headerOnly==false) && (this.contacts[j].show==true))
624 | {
625 | nextCandidateToLoad=this.contacts[j];
626 | break;
627 | }
628 | }
629 | }
630 | // make the contact active
631 | globalRefABListTable.children('.ablist_item.ablist_item_selected').removeClass('ablist_item_selected');
632 | if(nextCandidateToLoad!=null)
633 | {
634 | // prevent re-loading the contact if it is already loaded
635 | if((this.contactToReload!=null||$('#vCardEditor').attr('data-url')!=nextCandidateToLoad.uid) && !globalCardDAVInitLoad)
636 | {
637 | this.loadContactByUID(nextCandidateToLoad.uid);
638 | }
639 | else // because the collection click unselects the active contact we need to re-select it
640 | {
641 | // Make the selected contact active
642 | globalRefABListTable.children('.ablist_item.ablist_item_selected').removeClass('ablist_item_selected');
643 | globalRefABListTable.children('[data-id='+jqueryEscapeSelector(nextCandidateToLoad.uid)+']').addClass('ablist_item_selected');
644 | }
645 | // move scrollbar to ensure that the contact is visible in the interface
646 | if((selected_contact=globalRefABListTable.children('.ablist_item_selected')).length==1)
647 | globalRefABList.scrollTop(globalRefABList.scrollTop()+selected_contact.offset().top-globalRefABList.offset().top-globalRefABList.height()*globalKBNavigationPaddingRate);
648 | }
649 | else
650 | {
651 | this.contactLoaded=null;
652 | $('#ABContactColor').css('background-color', '');
653 | $('#ABContact').html('');
654 | }
655 | }
656 | if(this.contactToReload!=null&& (selected_contact=globalRefABListTable.find('[data-id="'+this.contactToReload.uid+'"]')).length==1)
657 | {
658 | selected_contact.addClass('ablist_item_selected');
659 | globalRefABList.scrollTop(globalRefABList.scrollTop()+selected_contact.offset().top-globalRefABList.offset().top-globalRefABList.height()*globalKBNavigationPaddingRate);
660 |
661 | }
662 | }
663 |
664 | this.getABCategories=function(returnSorted)
665 | {
666 | var categoriesArr=[];
667 |
668 | for(var category in this.contact_categories)
669 | categoriesArr.push(category);
670 |
671 | if(returnSorted)
672 | return categoriesArr.sort(function(x,y){return x.customCompare(y,globalSortAlphabet,1,false)});
673 | else
674 | return categoriesArr;
675 | }
676 |
677 | this.getABCompanies=function(returnSorted)
678 | {
679 | var companiesArr=[];
680 |
681 | for(var company in this.contact_companies)
682 | companiesArr.push(company);
683 |
684 | if(returnSorted)
685 | return companiesArr.sort(function(x,y){return x.customCompare(y,globalSortAlphabet,1,false)});
686 | else
687 | return companiesArr;
688 | }
689 |
690 | this.getABCompanyDepartments=function(inputCompany)
691 | {
692 | var departmentsArr=[];
693 |
694 | if(this.contact_companies[inputCompany]!=undefined)
695 | departmentsArr=this.contact_companies[inputCompany].departments.slice();
696 |
697 | return departmentsArr.sort(function(x,y){return x.customCompare(y,globalSortAlphabet,1,false)});
698 | }
699 |
700 | // Contact list is not sorted, instead "insert sort" is performed
701 | this.insertContact=function(inputContact, forceReload, disableDOM)
702 | {
703 | // Apple "group" vCards
704 | if(this.isContactGroup(inputContact.vcard))
705 | return this.insertContactGroup(inputContact, forceReload, false);
706 |
707 | // check for company contact
708 | inputContact.isCompany=false;
709 | var vcard_element=inputContact.vcard.match(vCard.pre['X-ABShowAs']);
710 | if(vcard_element!=null)
711 | {
712 | // parsed (contentline_parse) = [1]->"group.", [2]->"name", [3]->";param;param", [4]->"value"
713 | parsed=vcard_element[0].match(vCard.pre['contentline_parse']);
714 | if(vcardUnescapeValue(parsed[4]).match(RegExp('^company$','i')))
715 | inputContact.isCompany=true;
716 | }
717 |
718 | // check for company contact
719 | if((typeof globalContactsExtVcardToData)=='function')
720 | {
721 | inputContact.isLegacy=false;
722 | var vcard_element=inputContact.vcard.match(RegExp('\r\nX-IsLegacy:.*\r\n', 'mi'));
723 | if(vcard_element!=null)
724 | {
725 | // parsed (contentline_parse) = [1]->"group.", [2]->"name", [3]->";param;param", [4]->"value"
726 | var parsed=vcard_element[0].match(vCard.pre['contentline_parse']);
727 | if(vcardUnescapeValue(parsed[4]).match(RegExp('^(?:yes|1|true)$', 'i')))
728 | inputContact.isLegacy=true;
729 | }
730 | }
731 |
732 | // contact UID attr
733 | var vcard_element=inputContact.vcard.match(vCard.pre['contentline_UID']);
734 | if(vcard_element!=null)
735 | {
736 | // parsed (contentline_parse) = [1]->"group.", [2]->"name", [3]->";param;param", [4]->"value"
737 | parsed=vcard_element[0].match(vCard.pre['contentline_parse']);
738 | inputContact.uidattr=vcardUnescapeValue(parsed[4]);
739 | }else if(inputContact.etag){
740 | inputContact.uidattr=inputContact.etag; //etags in multistatus response are per-contact object, so they fly as UID in the absence of such
741 | }
742 | else // UID attr is REQUIRED
743 | return false; // invalud vcard
744 |
745 | var this_destination=this.contacts;
746 | var this_destination_hash=this.contacts_hash;
747 | var this_destination_hash_uidattr=this.contacts_hash_uidattr;
748 |
749 | // search plugin requirement
750 | inputContact.search_hide=false;
751 |
752 | // CATEGORIES suggestion
753 | var categoriesArr=(inputContact.categories=='' ? [] : vcardSplitValue(inputContact.categories,','));
754 | var allCategoriesArr=this.getABCategories(false);
755 |
756 | // The search funcionality uses this ASCII value (you can add additional data here)
757 |
758 | // ORG attribute
759 | var tmp=inputContact.vcard;
760 | var orgArr=[];
761 | var depArr=[];
762 | var tmpCurrentCompany='';
763 | var tmpCurrentDepartment='';
764 | while((vcard_element=tmp.match(vCard.pre['contentline_ORG']))!=null)
765 | {
766 | // parsed (contentline_parse) = [1]->"group.", [2]->"name", [3]->";param;param", [4]->"value"
767 | var parsed=vcard_element[0].match(vCard.pre['contentline_parse']);
768 | var parsed_valArr=vcardSplitValue(parsed[4], ';');
769 |
770 | if(isDataColumnDefined('COMPANY')) {
771 | setContactDataColumn(inputContact, 'COMPANY', vcardUnescapeValue(parsed_valArr[0]));
772 | }
773 |
774 | if(isDataColumnDefined('DEPARTMENT')) {
775 | setContactDataColumn(inputContact, 'DEPARTMENT', vcardUnescapeValue(parsed_valArr[1]));
776 | }
777 |
778 | tmpCurrentCompany=(parsed_valArr[0]==undefined || parsed_valArr[0]=='' ? '' : parsed_valArr[0]);
779 | tmpCurrentDepartment=(parsed_valArr[1]==undefined || parsed_valArr[1]=='' ? '' : parsed_valArr[1]);
780 |
781 | if(tmpCurrentCompany!='')
782 | orgArr[orgArr.length]=vcardUnescapeValue(tmpCurrentCompany);
783 |
784 | if(tmpCurrentDepartment)
785 | depArr[depArr.length]=vcardUnescapeValue(tmpCurrentDepartment);
786 |
787 | // remove the processed parameter
788 | tmp=tmp.replace(vcard_element[0],'\r\n');
789 | }
790 | var allOrgArr=this.getABCompanies(false);
791 |
792 | // N attribute
793 | while((vcard_element=tmp.match(vCard.pre['contentline_N']))!=null)
794 | {
795 | // parsed (contentline_parse) = [1]->"group.", [2]->"name", [3]->";param;param", [4]->"value"
796 | var parsed=vcard_element[0].match(vCard.pre['contentline_parse']);
797 | var parsed_valArr=vcardSplitValue(parsed[4],';');
798 |
799 | if(isDataColumnDefined('LASTNAME')) {
800 | setContactDataColumn(inputContact, 'LASTNAME', vcardUnescapeValue(parsed_valArr[0]));
801 | }
802 |
803 | if(isDataColumnDefined('FIRSTNAME')) {
804 | setContactDataColumn(inputContact, 'FIRSTNAME', vcardUnescapeValue(parsed_valArr[1]));
805 | }
806 |
807 | if(isDataColumnDefined('MIDDLENAME')) {
808 | setContactDataColumn(inputContact, 'MIDDLENAME', vcardUnescapeValue(parsed_valArr[2]));
809 | }
810 |
811 | if(isDataColumnDefined('PREFIX')) {
812 | setContactDataColumn(inputContact, 'PREFIX', vcardUnescapeValue(parsed_valArr[3]));
813 | }
814 |
815 | if(isDataColumnDefined('SUFFIX')) {
816 | setContactDataColumn(inputContact, 'SUFFIX', vcardUnescapeValue(parsed_valArr[4]));
817 | }
818 |
819 | // remove the processed parameter
820 | tmp=tmp.replace(vcard_element[0],'\r\n');
821 | }
822 |
823 | // NICKNAME attribute
824 | while((vcard_element=tmp.match(vCard.pre['contentline_NICKNAME']))!=null)
825 | {
826 | // parsed (contentline_parse) = [1]->"group.", [2]->"name", [3]->";param;param", [4]->"value"
827 | parsed=vcard_element[0].match(vCard.pre['contentline_parse']);
828 |
829 | if(isDataColumnDefined('NICKNAME')) {
830 | setContactDataColumn(inputContact, 'NICKNAME', parsed[4]);
831 | }
832 |
833 | // remove the processed parameter
834 | tmp=tmp.replace(vcard_element[0],'\r\n');
835 | }
836 |
837 | // X-PHONETIC-LAST-NAME attribute
838 | while((vcard_element=tmp.match(vCard.pre['contentline_X-PHONETIC-LAST-NAME']))!=null)
839 | {
840 | // parsed (contentline_parse) = [1]->"group.", [2]->"name", [3]->";param;param", [4]->"value"
841 | parsed=vcard_element[0].match(vCard.pre['contentline_parse']);
842 |
843 | if(isDataColumnDefined('PHONETICLASTNAME')) {
844 | setContactDataColumn(inputContact, 'PHONETICLASTNAME', parsed[4]);
845 | }
846 |
847 | // remove the processed parameter
848 | tmp=tmp.replace(vcard_element[0],'\r\n');
849 | }
850 |
851 | // X-PHONETIC-FIRST-NAME attribute
852 | while((vcard_element=tmp.match(vCard.pre['contentline_X-PHONETIC-FIRST-NAME']))!=null)
853 | {
854 | // parsed (contentline_parse) = [1]->"group.", [2]->"name", [3]->";param;param", [4]->"value"
855 | parsed=vcard_element[0].match(vCard.pre['contentline_parse']);
856 |
857 | if(isDataColumnDefined('PHONETICFIRSTNAME')) {
858 | setContactDataColumn(inputContact, 'PHONETICFIRSTNAME', parsed[4]);
859 | }
860 |
861 | // remove the processed parameter
862 | tmp=tmp.replace(vcard_element[0],'\r\n');
863 | }
864 |
865 | // BDAY attribute
866 | while((vcard_element=tmp.match(vCard.pre['contentline_BDAY']))!=null)
867 | {
868 | // parsed (contentline_parse) = [1]->"group.", [2]->"name", [3]->";param;param", [4]->"value"
869 | parsed=vcard_element[0].match(vCard.pre['contentline_parse']);
870 |
871 | if(isDataColumnDefined('BIRTHDAY')) {
872 | var bday = null;
873 | try {
874 | bday = $.datepicker.parseDate('yy-mm-dd', parsed[4]);
875 | }
876 | catch(e) {
877 |
878 | }
879 |
880 | if(bday) {
881 | setContactDataColumn(inputContact, 'BIRTHDAY', $.datepicker.formatDate(globalSettings.datepickerformat.value, bday));
882 | }
883 | }
884 |
885 | // remove the processed parameter
886 | tmp=tmp.replace(vcard_element[0],'\r\n');
887 | }
888 |
889 | // TITLE attribute
890 | while((vcard_element=tmp.match(vCard.pre['contentline_TITLE']))!=null)
891 | {
892 | // parsed (contentline_parse) = [1]->"group.", [2]->"name", [3]->";param;param", [4]->"value"
893 | parsed=vcard_element[0].match(vCard.pre['contentline_parse']);
894 |
895 | if(isDataColumnDefined('JOBTITLE')) {
896 | setContactDataColumn(inputContact, 'JOBTITLE', vcardUnescapeValue(parsed[4]));
897 | }
898 |
899 | // remove the processed parameter
900 | tmp=tmp.replace(vcard_element[0],'\r\n');
901 | }
902 |
903 | // NOTE attribute
904 | while((vcard_element=tmp.match(vCard.pre['contentline_NOTE']))!=null)
905 | {
906 | // parsed (contentline_parse) = [1]->"group.", [2]->"name", [3]->";param;param", [4]->"value"
907 | parsed=vcard_element[0].match(vCard.pre['contentline_parse']);
908 |
909 | if(isDataColumnDefined('NOTETEXT')) {
910 | setContactDataColumn(inputContact, 'NOTETEXT', vcardUnescapeValue(parsed[4]));
911 | }
912 |
913 | // remove the processed parameter
914 | tmp=tmp.replace(vcard_element[0],'\r\n');
915 | }
916 |
917 | // ADR attribute
918 | while((vcard_element=tmp.match(vCard.pre['contentline_ADR']))!=null)
919 | {
920 | // parsed (contentline_parse) = [1]->"group.", [2]->"name", [3]->";param;param", [4]->"value"
921 | var parsed=vcard_element[0].match(vCard.pre['contentline_parse']);
922 | var parsed_valArr=vcardSplitValue(parsed[4],';');
923 |
924 | if(isDataColumnDefined('ADDRESS')) {
925 | var unescapedArr = $.map(parsed_valArr, function(el) {
926 | if(el) {
927 | return vcardUnescapeValue(el);
928 | }
929 | });
930 |
931 | setContactDataColumn(inputContact, 'ADDRESS', unescapedArr.join(' '), {'TYPE': getParamsFromContentlineParse(tmp, parsed, 'TYPE', 'X-ABLabel', 'address_type_store_as')});
932 | }
933 |
934 | // remove the processed parameter
935 | tmp=tmp.replace(vcard_element[0],'\r\n');
936 | }
937 |
938 | // TEL attribute
939 | while((vcard_element=tmp.match(vCard.pre['contentline_TEL']))!=null)
940 | {
941 | // parsed (contentline_parse) = [1]->"group.", [2]->"name", [3]->";param;param", [4]->"value"
942 | parsed=vcard_element[0].match(vCard.pre['contentline_parse']);
943 |
944 | if(isDataColumnDefined('PHONE')) {
945 | setContactDataColumn(inputContact, 'PHONE', parsed[4], {'TYPE': getParamsFromContentlineParse(tmp, parsed, 'TYPE', 'X-ABLabel', 'phone_type_store_as')});
946 | }
947 |
948 | // remove the processed parameter
949 | tmp=tmp.replace(vcard_element[0],'\r\n');
950 | }
951 |
952 | // EMAIL attribute
953 | while((vcard_element=tmp.match(vCard.pre['contentline_EMAIL']))!=null)
954 | {
955 | // parsed (contentline_parse) = [1]->"group.", [2]->"name", [3]->";param;param", [4]->"value"
956 | parsed=vcard_element[0].match(vCard.pre['contentline_parse']);
957 |
958 | if(isDataColumnDefined('EMAIL')) {
959 | setContactDataColumn(inputContact, 'EMAIL', parsed[4], {'TYPE': getParamsFromContentlineParse(tmp, parsed, 'TYPE', 'X-ABLabel', 'email_type_store_as')});
960 | }
961 |
962 | // remove the processed parameter
963 | tmp=tmp.replace(vcard_element[0],'\r\n');
964 | }
965 |
966 | // URL attribute
967 | while((vcard_element=tmp.match(vCard.pre['contentline_URL']))!=null)
968 | {
969 | // parsed (contentline_parse) = [1]->"group.", [2]->"name", [3]->";param;param", [4]->"value"
970 | parsed=vcard_element[0].match(vCard.pre['contentline_parse']);
971 |
972 | if(isDataColumnDefined('URL')) {
973 | setContactDataColumn(inputContact, 'URL', parsed[4], {'TYPE': getParamsFromContentlineParse(tmp, parsed, 'TYPE', 'X-ABLabel', 'url_type_store_as')});
974 | }
975 |
976 | // remove the processed parameter
977 | tmp=tmp.replace(vcard_element[0],'\r\n');
978 | }
979 |
980 | // X-ABDATE attribute
981 | while((vcard_element=tmp.match(vCard.pre['contentline_X-ABDATE']))!=null)
982 | {
983 | // parsed (contentline_parse) = [1]->"group.", [2]->"name", [3]->";param;param", [4]->"value"
984 | parsed=vcard_element[0].match(vCard.pre['contentline_parse']);
985 |
986 | if(isDataColumnDefined('DATES')) {
987 | var abdate = null;
988 | try {
989 | abdate = $.datepicker.parseDate('yy-mm-dd', parsed[4]);
990 | }
991 | catch(e) {
992 |
993 | }
994 |
995 | if(abdate) {
996 | setContactDataColumn(inputContact, 'DATES', $.datepicker.formatDate(globalSettings.datepickerformat.value, abdate), {'TYPE': getParamsFromContentlineParse(tmp, parsed, 'TYPE', 'X-ABLabel', 'date_store_as')});
997 | }
998 | }
999 |
1000 | // remove the processed parameter
1001 | tmp=tmp.replace(vcard_element[0],'\r\n');
1002 | }
1003 |
1004 | // X-ABRELATEDNAMES attribute
1005 | while((vcard_element=tmp.match(vCard.pre['contentline_X-ABRELATEDNAMES']))!=null)
1006 | {
1007 | // parsed (contentline_parse) = [1]->"group.", [2]->"name", [3]->";param;param", [4]->"value"
1008 | parsed=vcard_element[0].match(vCard.pre['contentline_parse']);
1009 |
1010 | if(isDataColumnDefined('RELATED')) {
1011 | setContactDataColumn(inputContact, 'RELATED', parsed[4], {'TYPE': getParamsFromContentlineParse(tmp, parsed, 'TYPE', 'X-ABLabel', 'person_type_store_as')});
1012 | }
1013 |
1014 | // remove the processed parameter
1015 | tmp=tmp.replace(vcard_element[0],'\r\n');
1016 | }
1017 |
1018 | // X-SOCIALPROFILE attribute
1019 | while((vcard_element=tmp.match(vCard.pre['contentline_X-SOCIALPROFILE']))!=null)
1020 | {
1021 | // parsed (contentline_parse) = [1]->"group.", [2]->"name", [3]->";param;param", [4]->"value"
1022 | parsed=vcard_element[0].match(vCard.pre['contentline_parse']);
1023 |
1024 | if(isDataColumnDefined('PROFILE')) {
1025 | setContactDataColumn(inputContact, 'PROFILE', getParamsFromContentlineParse(tmp, parsed, 'X-USER', null, null, true)[0], {'TYPE': getParamsFromContentlineParse(tmp, parsed, 'TYPE', 'X-ABLabel', 'profile_type_store_as')});
1026 | }
1027 |
1028 | // remove the processed parameter
1029 | tmp=tmp.replace(vcard_element[0],'\r\n');
1030 | }
1031 |
1032 | // IMPP attribute
1033 | while((vcard_element=tmp.match(vCard.pre['contentline_IMPP']))!=null)
1034 | {
1035 | // parsed (contentline_parse) = [1]->"group.", [2]->"name", [3]->";param;param", [4]->"value"
1036 | parsed=vcard_element[0].match(vCard.pre['contentline_parse']);
1037 | if(isDataColumnDefined('IM')) {
1038 | setContactDataColumn(inputContact, 'IM', parsed[4].replace(vCard.pre['vcardToData_before_val'], ''), {
1039 | 'TYPE': getParamsFromContentlineParse(tmp, parsed, 'TYPE', 'X-ABLabel', 'im_type_store_as'),
1040 | 'SERVICE-TYPE': getParamsFromContentlineParse(tmp, parsed, 'X-SERVICE-TYPE', null, 'im_service_type_store_as')
1041 | });
1042 | }
1043 |
1044 | // remove the processed parameter
1045 | tmp=tmp.replace(vcard_element[0],'\r\n');
1046 | }
1047 |
1048 | // CATEGORIES attribute (preparsed)
1049 | if(isDataColumnDefined('CATEGORIES')) {
1050 | setContactDataColumn(inputContact, 'CATEGORIES', inputContact.categories.splitCustom(','));
1051 | }
1052 |
1053 | 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)
1054 | return false; //invalid vcard
1055 |
1056 | // if company headers are used add also the header to the searchvalue
1057 | var companyHeader='';
1058 | if(typeof globalGroupContactsByCompanies!='undefined' && globalGroupContactsByCompanies==true)
1059 | {
1060 | if(tmpCurrentCompany!='' || tmpCurrentDepartment!='')
1061 | {
1062 | if(typeof (getCRMSortKey)=='function')
1063 | companyHeader=getCRMSortKey(inputContact);
1064 | else
1065 | companyHeader=tmpCurrentCompany+'\u0009'+tmpCurrentDepartment+'\u0009';
1066 | }
1067 | }
1068 |
1069 | inputContact.searchvalue=(companyHeader+inputContact.displayvalue).multiReplace(globalSearchTransformAlphabet);
1070 |
1071 | // CATEGORIES suggestion
1072 | for(var i=0;i90 && unicodeValue<97) || (unicodeValue>122 && unicodeValue<127))
1172 | {
1173 | headerValue='#';
1174 | inputContact.sortkey='#'+inputContact.sortkey;
1175 | }
1176 | else
1177 | headerValue=inputContact.sortkey.charAt(0).toUpperCase();
1178 | }
1179 | else
1180 | {
1181 | headerValue='#';
1182 | inputContact.sortkey='#';
1183 | }
1184 |
1185 | headerSortKey=headerValue;
1186 | }
1187 |
1188 | // create the header
1189 | var headerObject={headerOnly: true, sortkey: headerSortKey, displayvalue: headerValue};
1190 |
1191 | // find the index where to insert the new contact O(n*log(n))
1192 | insertIndex=0;
1193 | low=0;
1194 | high=this_destination.length-1;
1195 | if(this_destination.length>0)
1196 | while(low').text(getContactDataColumn(inputContact, columns[i])).appendTo(newElement);
1262 | }
1263 |
1264 | newElement.click(function() {
1265 | if($(this).hasClass('ablist_item_selected') || globalObjectLoading)
1266 | return false;
1267 | else
1268 | globalAddressbookList.loadContactByUID(this.getAttribute('data-id'));
1269 | });
1270 |
1271 | // set the company icon
1272 | if(inputContact.isCompany==true)
1273 | newElement.addClass('company');
1274 |
1275 | if(typeof globalDisableDragAndDrop=='undefined' || globalDisableDragAndDrop!=true)
1276 | newElement.draggable({
1277 | delay: 250,
1278 | revert: 'invalid',
1279 | scroll: false,
1280 | opacity: 0.8,
1281 | stack: '#SystemCardDavMATE',
1282 | containment: '#SystemCardDavMATE',
1283 | appendTo: 'body',
1284 | start: function( event, ui ){
1285 | // disallow on read-only collection
1286 | if(globalResourceCardDAVList.getCollectionPrivByUID($(this).attr('data-id').replace(RegExp('[^/]*$'),''))==true)
1287 | return false;
1288 | },
1289 | helper: function(){
1290 | $('#ResourceCardDAVList').find('.resourceCardDAV.ui-droppable').droppable( 'option', 'accept', false);
1291 | $('#ResourceCardDAVList').find('.group.ui-droppable').droppable( 'option', 'accept', false);
1292 |
1293 | $('#ResourceCardDAVList').find('.resourceCardDAV[data-id!='+jqueryEscapeSelector($(this).attr('data-id').replace(RegExp('[^/]+$'),''))+'].ui-droppable').droppable( 'option', 'accept', '.ablist_item');
1294 | var myContactGroups=globalAddressbookList.getMyContactGroups($(this).attr('data-id'));
1295 | $('#ResourceCardDAVList').find('.group[data-id^='+jqueryEscapeSelector($(this).attr('data-id').replace(RegExp('[^/]+$'),''))+'].ui-droppable').each(function(index, element){
1296 | if(myContactGroups.indexOf($(element).attr('data-id'))==-1)
1297 | $(element).droppable( 'option', 'accept', '.ablist_item');
1298 | });
1299 |
1300 | var tmp=$(this).clone();
1301 | tmp.addClass('ablist_item_dragged');
1302 | // we cannot use .css() here, because we need to add !important (problem with Gecko based browsers)
1303 | var tmp_style='max-width: '+$(this).outerWidth()+'px;';
1304 | if($(this).css('background-image')!='none')
1305 | tmp_style+='background-image: url(images/company_s_w.svg) !important;';
1306 | tmp.attr('style', tmp_style);
1307 |
1308 | return tmp;
1309 | }
1310 | });
1311 |
1312 | globalRefABListTable.children().eq(insertIndex+headerMiss-1).after(newElement);
1313 |
1314 | if($('#vCardEditor').attr('data-editor-state')=='edit')
1315 | {
1316 | if((selected_contact=globalRefABListTable.children('.ablist_item_selected')).length==1)
1317 | globalRefABList.scrollTop(globalRefABList.scrollTop()+selected_contact.offset().top-globalRefABList.offset().top-globalRefABList.height()*globalKBNavigationPaddingRate);
1318 | }
1319 | // toto tu asi nahradit zavolanim trigger('click') co vyrazne sprehladni kod
1320 | // =>
1321 | // load the updated contact (because we first deleted it, we need to set it active)
1322 | if(makeActive!=null)
1323 | {
1324 | // make the contact active
1325 | globalRefABListTable.children('.ablist_item.ablist_item_selected').removeClass('ablist_item_selected');
1326 | globalRefABListTable.children().eq(insertIndex+headerMiss).addClass('ablist_item_selected');
1327 | this.loadContactByUID(makeActive);
1328 | }
1329 | }
1330 | }
1331 |
1332 | this.renderContacs=function()
1333 | {
1334 | var this_destination=this.contacts;
1335 | var this_destination_hash=this.contacts_hash;
1336 |
1337 | var tmpResultObject=[];
1338 |
1339 | for(var i=0;i').text(getContactDataColumn(this_destination[i], columns[j])).appendTo(newElement);
1361 | }
1362 | for(; j').appendTo(newElement);
1364 | }
1365 |
1366 | newElement.click(function() {
1367 | if($(this).hasClass('ablist_item_selected') || globalObjectLoading)
1368 | return false;
1369 | else
1370 | globalAddressbookList.loadContactByUID(this.getAttribute('data-id'));
1371 | });
1372 |
1373 | // set the company icon
1374 | if(this_destination[i].isCompany==true)
1375 | newElement.addClass('company');
1376 |
1377 | if(typeof globalDisableDragAndDrop=='undefined' || globalDisableDragAndDrop!=true)
1378 | newElement.draggable({
1379 | delay: 250,
1380 | revert: 'invalid',
1381 | scroll: false,
1382 | opacity: 0.8,
1383 | stack: '#SystemCardDavMATE',
1384 | containment: '#SystemCardDavMATE',
1385 | appendTo: 'body',
1386 | start: function( event, ui ){
1387 | // disallow on read-only collection
1388 | if(globalResourceCardDAVList.getCollectionPrivByUID($(this).attr('data-id').replace(RegExp('[^/]*$'),''))==true)
1389 | return false;
1390 | },
1391 | helper: function(){
1392 | $('#ResourceCardDAVList').find('.resourceCardDAV.ui-droppable').droppable( 'option', 'accept', false);
1393 | $('#ResourceCardDAVList').find('.group.ui-droppable').droppable( 'option', 'accept', false);
1394 |
1395 | $('#ResourceCardDAVList').find('.resourceCardDAV[data-id!='+jqueryEscapeSelector($(this).attr('data-id').replace(RegExp('[^/]+$'),''))+'].ui-droppable').droppable( 'option', 'accept', '.ablist_item');
1396 | var myContactGroups=globalAddressbookList.getMyContactGroups($(this).attr('data-id'));
1397 | $('#ResourceCardDAVList').find('.group[data-id^='+jqueryEscapeSelector($(this).attr('data-id').replace(RegExp('[^/]+$'),''))+'].ui-droppable').each(function(index, element){
1398 | if(myContactGroups.indexOf($(element).attr('data-id'))==-1)
1399 | $(element).droppable( 'option', 'accept', '.ablist_item');
1400 | });
1401 |
1402 | var tmp=$(this).clone();
1403 | tmp.addClass('ablist_item_dragged');
1404 | // we cannot use .css() here, because we need to add !important (problem with Gecko based browsers)
1405 | var tmp_style='max-width: '+$(this).outerWidth()+'px;';
1406 | if($(this).css('background-image')!='none')
1407 | tmp_style+='background-image: url(images/company_s_w.svg) !important;';
1408 | tmp.attr('style', tmp_style);
1409 |
1410 | return tmp;
1411 | }
1412 | });
1413 | }
1414 | tmpResultObject.push(newElement);
1415 | }
1416 |
1417 | globalRefABListTable.empty().append(tmpResultObject);
1418 | }
1419 |
1420 | this.removeContact=function(inputUid, loadNext, isFromPUT)
1421 | {
1422 | if(!(inputUid instanceof Array))
1423 | inputUid=[inputUid];
1424 | var tmpRex=new RegExp('/[^/]*$','');
1425 |
1426 | // Apple "group" vCards
1427 | for(var i=inputUid.length-1;i>=0;i--)
1428 | for(var j=this.vcard_groups[inputUid[i].replace(tmpRex,'/')].length-1;j>=0;j--)
1429 | if(inputUid.indexOf(this.vcard_groups[inputUid[i].replace(tmpRex,'/')][j].uid)!=-1)
1430 | return this.removeContactGroup(inputUid[i], loadNext);
1431 |
1432 | for(var i=this.contacts.length-1;i>=0;i--)
1433 | if(this.contacts[i]!=undefined&&inputUid.indexOf(this.contacts[i].uid)!=-1)
1434 | {
1435 | var inUID=this.contacts[i].uid;
1436 | // CATEGORIES suggestion
1437 | var categoriesArr=vcardSplitValue(this.contacts[i].categories,',');
1438 | for(var j=0;j"group.", [2]->"name", [3]->";param;param", [4]->"value"
1457 | parsed=vcard_element[0].match(vCard.pre['contentline_parse']);
1458 | orgArr[orgArr.length]=vcardUnescapeValue(vcardSplitValue(parsed[4],';')[0]);
1459 |
1460 | // remove the processed parameter
1461 | tmp=tmp.replace(vcard_element[0],'\r\n');
1462 | }
1463 | for(var j=0;j=0;j--)
1491 | if(this.contacts[j].headerOnly!=true && this.contacts[j].show==true)
1492 | {
1493 | nextCandidateToLoad=this.contacts[j];
1494 | break;
1495 | }
1496 | }
1497 |
1498 | // remove the item
1499 | item.remove();
1500 | this.contacts.splice(i,1);
1501 | if(this.contacts_hash[inUID]!=undefined)
1502 | {
1503 | delete this.contacts_hash_uidattr[this.contacts_hash[inUID].uidattr];
1504 | delete this.contacts_hash[inUID];
1505 | }
1506 | else if(this.companies_hash[inUID]!=undefined)
1507 | {
1508 | delete this.companies_hash_uidattr[this.contacts_hash[inUID].uidattr];
1509 | delete this.companies_hash[inUID];
1510 | }
1511 |
1512 | // remove the header if there is no more contact
1513 | var removeHeader=true;
1514 | var prevHeader=null;
1515 | // find the previous header index
1516 | for(var j=i-1;j>=0;j--)
1517 | if(this.contacts[j].headerOnly!=undefined && this.contacts[j].headerOnly==true)
1518 | {
1519 | prevHeader=j;
1520 | break;
1521 | }
1522 |
1523 | // check for contact existence for the found header
1524 | if((prevHeader+1)=0;i--)
1583 | 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 no animation */
1608 | globalObjectLoading=false; // re-enable keyboard navigation
1609 | });
1610 | else
1611 | {
1612 | $('#ABContactColor').css('background-color', '');
1613 | $('#ABContact').empty();
1614 | globalDisableAnimationMessageHiding='contactRfcNotCompliant';
1615 | var tmpTime=show_editor_message('out','message_error', localization[globalInterfaceLanguage].contactRfcNotCompliant, globalHideInfoMessageAfter);
1616 | setTimeout(function(){globalObjectLoading=false;}, tmpTime); // re-enable keyboard navigation
1617 | }
1618 | // Make the selected contact active
1619 | globalRefABListTable.children('.ablist_item.ablist_item_selected').removeClass('ablist_item_selected');
1620 | globalRefABListTable.children('[data-id='+jqueryEscapeSelector(this.contacts_hash[inputUID].uid)+']').addClass('ablist_item_selected');
1621 | this.contactToReload=null;
1622 | if(globalRefABListTable.children('[data-id='+jqueryEscapeSelector(this.contacts_hash[inputUID].uid)+']:visible').length>0&&$('#ABInMessageEditBox').css('display')!='none')
1623 | {
1624 | animate_message('#ABInMessageEditBox', '#ABInMessageTextEditBox', 0, '-=');
1625 | $('#ABInMessageEditBox').css('display','');
1626 |
1627 | }
1628 | else if(globalRefABListTable.children('[data-id='+jqueryEscapeSelector(this.contacts_hash[inputUID].uid)+']:visible').length==0&&$('#ABInMessageEditBox').css('display')=='none')
1629 | {
1630 | this.contactToReload=this.contacts_hash[inputUID];
1631 | globalDisableAnimationMessageHiding='errContactHidden';
1632 | $('#ABInMessageEditBox').css('display','block');
1633 | $('#ABInMessageTextEditBox').attr('class','message_success');
1634 | $('#ABInMessageTextEditBox').text(localization[globalInterfaceLanguage][globalDisableAnimationMessageHiding]);
1635 | animate_message('#ABInMessageEditBox', '#ABInMessageTextEditBox', globalHideInfoMessageAfter);
1636 | }
1637 | if($('#ResourceCardDAVListOverlay').is(':visible'))
1638 | {
1639 | if($('#ABContactOverlay').is(':visible'))
1640 | {
1641 | var animation = 400;
1642 | var duration = globalHideInfoMessageAfter + 2*animation;
1643 | setTimeout(function(){
1644 | $('#ResourceCardDAVListOverlay').fadeOut(animation);
1645 | $('#ABListOverlay').fadeOut(animation,function(){});
1646 | $('#ABContactOverlay').fadeOut(animation,function(){globalRefAddContact.prop('disabled',false);});
1647 | },duration-animation);
1648 | }
1649 | else
1650 | {
1651 | $('#ResourceCardDAVListOverlay').fadeOut(globalEditorFadeAnimation);
1652 | $('#ABListOverlay').fadeOut(globalEditorFadeAnimation,function(){});
1653 | }
1654 | }
1655 | }
1656 | else
1657 | {
1658 | $('#ABContactColor').css('background-color', '');
1659 | $('#ABContact').empty();
1660 | // CardDAVeditor_cleanup(false, false); // editor initialization
1661 | }
1662 | checkContactFormScrollBar();
1663 | }
1664 |
1665 | this.loadContactByVcard=function(vcard, color, isCompany, inputEditorMode, inputEditorLockedEntries)
1666 | {
1667 | // sem callback pre index.html a v pripade ak pridavame usera (nie firmu) pridat do vcard prislusny atribut
1668 | if(typeof(globalContactsExtLoadByVcardBefore)=='function')
1669 | vcard=globalContactsExtLoadByVcardBefore(vcard, isCompany);
1670 |
1671 | var loadContact=new Object();
1672 | loadContact.vcard=vcard;
1673 | loadContact.isCompany=isCompany;
1674 | loadContact.color=color;
1675 |
1676 | globalObjectLoading=true; // temporary disable keyboard navigation
1677 | if(vcardToData(loadContact, false /* XXX check this */, isCompany, inputEditorMode, inputEditorLockedEntries))
1678 | $('#EditorBox').fadeTo(0, 1, function(){ /* 0 => no animation */
1679 | // append the UID of previous contact into "data-id" for "cancel" functionality
1680 | $('#vCardEditor').find('[data-type="cancel"]').attr('data-id', globalAddressbookList.getLoadedContactUID());
1681 | this.contactLoaded=null; // do not do this earlier
1682 | globalObjectLoading=false; // re-enable keyboard navigation
1683 | });
1684 | else
1685 | {
1686 | // todo: replace with icon or text in the editor div
1687 | globalDisableAnimationMessageHiding='contactRfcNotCompliant';
1688 | show_editor_message('out', 'message_error', localization[globalInterfaceLanguage].contactRfcNotCompliant, globalHideInfoMessageAfter);
1689 | this.contactLoaded=null; // do not do this earlier
1690 | globalObjectLoading=false; // re-enable keyboard navigation
1691 | }
1692 | checkContactFormScrollBar();
1693 | }
1694 |
1695 | // DONE
1696 | this.getContactByUID=function(inputUID)
1697 | {
1698 | // find the inputUID contact
1699 | if(this.contacts_hash[inputUID]!=undefined)
1700 | return this.contacts_hash[inputUID];
1701 | else
1702 | return null;
1703 | }
1704 |
1705 | // DONE
1706 | this.getContactGroupByUID=function(inputUID)
1707 | {
1708 | var collectionUID=inputUID.replace(RegExp('[^/]*$'),'');
1709 | for(var i=0;i