PageSourceSearch

http://merrionettepark.org/Assets/Scripts/WidgetAndFileDroppable.js

js merrionettepark.org collected 2026-10-02 20:08:45 UTC 17,529 bytes, 395 lines download raw bytes

1function makeWidgetAreaDroppable(widgetID, $element, widgetType, allowMultipleFiles, callback) {
2	if (!window.ContentCollectionInstance) {
3		window.ContentCollectionInstance = new ContentCollectionClass();
4		window.ContentCollectionInstance.init();
5	}
6
7	window.FileDroppableInstance = new widgetNS.FileDroppableClass();
8
9	window.FileDroppableInstance.Init(widgetID, $element, widgetType, allowMultipleFiles, callback);
10	window.FileDroppableInstance.MakeFileDroppable();
11}
12
13//Namespace
14var widgetNS = createNamespace('CivicPlus.Javascript.Widgets');
15widgetNS.fileDropSlideshowType = 0;
16/************************************************************************/
17/*************************File Droppable Class Definition*****************
18/************************************************************************/
19
20widgetNS.FileDroppableClass = function () {
21
22	//Properties
23	this.documentIDCollection = ''; //List of documentIDs being uploaded.
24	this.createdWidgetID = ''; //In case of widget creation, ID of the new widget created.
25	this.storedInSession = false; //Whether or not the HttpPostedFileBase info is stored in session. Only populated in case of widget creation mechanicsm.
26	this.$allTargets = $('.pageContent').find('.target'); //Grid targets.
27	this.$allIndicators = $('.indicator'); //Grid indicators.
28	this.$element = null; //Element in the DOM on which files are being dropped
29	this.widgetID = ''; //WidgetID of the widget on which the files are being dropped. '' in case of widget creation, createdWidgetID is
30	this.widgetType = 0;
31	this.dropType = null;
32	this.allowMultipleFiles = false;
33	this.callback = null;
34	this.DocOverEventRanOnce = false;
35
36	//Enums
37	this.slideshowActionTypeENUM = { OptionNotChosenYet: 0, CreateNew: 1, ModifyExisting: 2 };
38	this.dropTypeENUM = { CreateWidget: 1, AddToExistingWidget: 2 };
39	this.widgetTypeENUM = { None: 0, RelatedDocuments: 5, Image: 2, Slideshow: 3 };
40
41	//Constant variables.
42	this.MODULE_ID = 75;
43	this.MAX_FILE_SIZE = 20; //File size in MB	
44	this.VALID_IMAGE_TYPES = ['image/jpeg', 'image/jpg', 'image/png', 'image/gif', 'image/bmp', 'image/tiff', 'image/raw', 'image/ico'];
45	this.MAX_FILES_FOR_MULTIPLE_UPLOADS = 50; //Max number of files allowed to be uploaded at once.
46}
47
48widgetNS.FileDroppableClass.prototype.MakeFileDroppable = function () {
49	var classRef = this;
50	classRef.$element.filedrop({
51		//fallback_id: 'upload_button',   // an identifier of a standard file input element, becomes the target of "click" events on the dropzone
52		url: '/Admin/DocumentCenter/FileDrop/Step2_CreateDocument', // upload handler, handles each file separately, can also be a function taking the file and returning a url
53		paramname: 'file', // POST parameter name used on serverside to reference file, can also be a function taking the filename and returning the paramname
54		withCredentials: true, // make a cross-origin request with cookies
55		data: {
56			contentCollectionID: window.ContentCollectionInstance.ContentCollectionID,
57			widgetID: classRef.$element.hasClass('target') ? classRef.createdWidgetID : classRef.widgetID,
58			widgetType: classRef.widgetType
59		},
60		error: function (err, file) {
61			switch (err) {
62				case 'BrowserNotSupported':
63					alert('browser does not support HTML5 drag and drop');
64					classRef.DropHoverEffect();
65					break;
66				case 'TooManyFiles':
67					alert('More than one file cannot be uploaded for this widget.');
68					classRef.DropHoverEffect();
69					break;
70				case 'FileTooLarge':
71					// program encountered a file whose size is greater than 'maxfilesize'
72					alert(file.name + ' has a file size that is too large.');
73					classRef.DropHoverEffect();
74					break;
75				case 'FileTypeNotAllowed':
76					alert('This file cannot be dropped onto this widget. Please check your file extension.');
77					classRef.DropHoverEffect();
78					break;
79				case 'FileExtensionNotAllowed':
80					// The file extension is not in the specified list 'allowedfileextensions'
81					classRef.DropHoverEffect();
82					break;
83				case 'Bad Request':
84					alert('Server refusing to accept the file. Please check file name or type.');
85					break;
86				default:
87					classRef.DropHoverEffect();
88					break;
89			}
90		},
91		allowedfiletypes: classRef.dropType === classRef.dropTypeENUM.CreateWidget ? [] : (classRef.widgetType === classRef.widgetTypeENUM.RelatedDocuments ? [] : ['image/jpeg', 'image/jpg', 'image/png', 'image/gif', 'image/bmp', 'image/tiff', 'image/raw', 'image/ico']), // filetypes allowed by Content-Type.  Empty array means no restrictions
92		allowedfileextensions: classRef.dropType === classRef.dropTypeENUM.CreateWidget ? [] : (classRef.widgetType === classRef.widgetTypeENUM.RelatedDocuments ? [] : ['.jpg', '.jpeg', '.jfif', '.png', '.gif', '.bmp', '.tiff', '.raw', '.ico']), // file extensions allowed. Empty array means no restrictions
93		maxfiles: classRef.allowMultipleFiles ? classRef.MAX_FILES_FOR_MULTIPLE_UPLOADS : 1,
94		maxfilesize: classRef.MAX_FILE_SIZE,
95		dragOver: function() { classRef.CreateHoverEffect(); },
96		dragLeave: function() { classRef.DropHoverEffect(); },
97		docOver: function() { classRef.EnableDroppableGridTargets(); },
98		docLeave: function() { classRef.DisableDroppableGridTargets(); },
99		drop: function(e) {
100			//Do not execute any logic here if a widget has been dropped from the Widgets tab under LiveEdit tabs. Since the widget becomes fileDroppable, it tries to execute this logic when anything is dropped on it, but in case of widgets from layout tab, nothing needs to be executed here.
101			let continueDrop = false;
102			const files = e.originalEvent?.dataTransfer?.files;
103			if (files?.length > 0) {
104				const letters = /^[\w.,=~!\s\-\)\(\[\]\{\}]+$/g;
105				for (let i = 0; i < files.length; i++) {
106					const file = files[i];
107					if (!file.name.match(letters)) {
108						alert('You have an invalid character in your filename: ' + file.name + '. These are the accepted characters: a-z A-Z 0-9 ~ !( ) - _  = [ ] { } , .');
109						classRef.DropHoverEffect();
110						return false;
111					}
112				}
113				let url = '/Admin/DocumentCenter/FolderForModal/HasRights';
114				$.ajaxSetup({ async: false });
115				AJAX(url, 'POST', '', function (response) {
116					if (response.HasRights) {
117						continueDrop = true;
118						if (classRef.dropType === classRef.dropTypeENUM.CreateWidget) {
119							classRef.CreateWidget(e);
120						}
121						AJAX('/Admin/DocumentCenter/FileDrop/Step1_CreateFolder', 'POST', { widgetType: classRef.widgetType }, null, false);
122						classRef.EnableDroppableGridTargetsRanOnce = false;
123					} else {
124						alert(response.Message);
125					}
126				}, false);
127				$.ajaxSetup({ async: true});
128			}
129			return continueDrop;
130		},
131		uploadStarted: function (i, file, len) {
132			if (classRef.dropType === classRef.dropTypeENUM.CreateWidget) {
133				var widgetTypeString = (classRef.widgetType === classRef.widgetTypeENUM.Image ? 'Image' : (classRef.widgetType === classRef.widgetTypeENUM.RelatedDocuments ? 'Related Documents' : 'SlideShow'));
134				ajaxPostBackStart('Creating ' + widgetTypeString + ' Widget');
135			} else
136				ajaxPostBackStart('Saving');
137		},
138		uploadFinished: function (i, file, response, time) {
139			if (response.StoredInSession == 1) {
140				classRef.storedInSession = true;
141			}
142			if (response.DocumentIDCollection != null && response.DocumentIDCollection != 0)
143				classRef.documentIDCollection += response.DocumentIDCollection + ',';
144		},
145		beforeSend: function (file, i, done, e) {
146			if (classRef.widgetType === classRef.widgetTypeENUM.Slideshow) {
147				if (classRef.ExistingSlideshow()) // a slideshow already present.
148				{
149					$popUp_Slideshow = openCpModal({
150						title: 'Slideshow',
151						className: 'newCP adminWrap cpPopOver admin modalSlideshow',
152						isFrontEnd: false,
153						useIframe: false,
154						draggable: true,
155						url: '/Admin/DocumentCenter/FileDrop/SlideshowCreateModifyModal'
156					});
157					var interval = window.setInterval(function (e) {
158						if (widgetNS.fileDropSlideshowType !== classRef.slideshowActionTypeENUM.OptionNotChosenYet) {
159							done();
160							window.clearInterval(interval);
161						}
162					}, 1000);
163				} else
164					done(); //Create a new slideshow withot asking options from user, since its a new slideshow.
165			} else {
166				done();
167			}
168		},
169		afterAll: function () {
170			if (classRef.storedInSession) {
171				classRef.ExecutePostWidgetCreationProcess();
172			} else {
173				classRef.ExecutePostFilesUploadProcess();
174			}
175		}
176	});
177};
178
179widgetNS.FileDroppableClass.prototype.Init = function (widgetID, $element, widgetType, allowMultipleFiles, callback) {
180	var classRef = this;
181	classRef.widgetID = widgetID;
182	classRef.$element = $element;
183	classRef.widgetType = widgetType;
184	classRef.allowMultipleFiles = allowMultipleFiles;
185	classRef.callback = callback;
186	classRef.dropType = classRef.DetermineDropType();
187};
188
189widgetNS.FileDroppableClass.prototype.DetermineWidgetType = function (event) {
190	var classRef = this;
191	var nonImageFile = false;
192	var dataTransfer = event.originalEvent?.dataTransfer;
193	if (!dataTransfer) {
194		return; // no data transfer bail.
195	}
196	if (dataTransfer.files.length === 1 && classRef.VALID_IMAGE_TYPES.indexOf(dataTransfer.files[0].type) !== -1)
197		return classRef.widgetTypeENUM.Image;
198
199	for (var i = 0; i < dataTransfer.files.length; i++) {
200		if (classRef.VALID_IMAGE_TYPES.indexOf(dataTransfer.files[i].type) === -1) {
201			nonImageFile = true;
202		}
203	}
204	return nonImageFile ? classRef.widgetTypeENUM.RelatedDocuments : classRef.widgetTypeENUM.Slideshow;
205};
206
207widgetNS.FileDroppableClass.prototype.EnableDroppableGridTargets = function () {
208	const classRef = this;
209	const $body = $('body');
210	if (!$body.hasClass('docOverRan')) {
211		$body.addClass('docOverRan');
212		classRef.$allTargets.removeClass("targetOver");
213		classRef.$allIndicators.removeClass('active');
214		$('.pageContent.preDragging').removeClass('preDragging').addClass('dragging');
215	}
216};
217
218widgetNS.FileDroppableClass.prototype.DisableDroppableGridTargets = function () {
219	const classRef = this;
220	const $body = $('body');
221	if ($body.hasClass('docOverRan')) {
222		$('.pageContent.dragging').removeClass('dragging').addClass('preDragging');
223		classRef.$allTargets.removeClass("targetOver");
224		classRef.$allIndicators.removeClass('active');
225		$body.removeClass('docOverRan');
226	}
227};
228
229widgetNS.FileDroppableClass.prototype.CreateHoverEffect = function () {
230	const classRef = this;
231	if (classRef.$element.hasClass('target') && !classRef.$element.hasClass("targetOver")) {
232		classRef.$allTargets.removeClass("targetOver");
233		classRef.$allIndicators.removeClass('active');
234		classRef.$element.addClass("targetOver");
235		const indicatorID = `#indicator_${classRef.$element.data('contentcontainerid')}_${classRef.$element.data('widgetcontainerid')}_${classRef.$element.data('sequence')}`;
236		$(indicatorID).addClass('active');
237		return;
238	}
239	if (!classRef.$element.hasClass('targeted') && !classRef.$element.hasClass('target')) {
240		classRef.$allTargets.removeClass("targetOver");
241		classRef.$allIndicators.removeClass('active');
242		classRef.$element.addClass('targeted');
243	}
244};
245
246widgetNS.FileDroppableClass.prototype.DropHoverEffect = function () {
247	const classRef = this;
248	$('#divPermissions').remove();
249	classRef.$element.removeClass('targeted');
250	const indicatorID = `#indicator_${classRef.$element.data('contentcontainerid')}_${classRef.$element.data('widgetcontainerid')}_${classRef.$element.data('sequence')}`;
251	$(indicatorID).removeClass('active');
252};
253
254widgetNS.FileDroppableClass.prototype.GetPermissionString = function (groupName) {
255	var permString = '';
256	$('input[name=' + groupName + ']').each(function (e) {
257		permString = $(this).val() + ',';
258	});
259	return permString;
260};
261
262widgetNS.FileDroppableClass.prototype.DetermineDropType = function () {
263	var classRef = this;
264	if (classRef.$element.hasClass('target'))
265		return classRef.dropTypeENUM.CreateWidget;
266	else
267		return classRef.dropTypeENUM.AddToExistingWidget;
268};
269
270widgetNS.FileDroppableClass.prototype.CreateWidget = function (event) {
271	var classRef = this;
272	var contentContainerID = classRef.$element.data('contentcontainerid');
273	var widgetContainerID = classRef.$element.data('widgetcontainerid');
274	var sequence = classRef.$element.data('sequence');
275	classRef.widgetType = classRef.DetermineWidgetType(event);
276	if (!classRef.widgetType) {
277		return;
278	}
279	var url = '/Layout/WidgetAdd/Add?contentCollectionID=' + window.ContentCollectionInstance.ContentCollectionID + '&widgetContainerID=' + widgetContainerID + '&sequence=' + sequence + '&widgetType=' + classRef.widgetType + '&contentContainerID=' + contentContainerID + '&parentID=null';
280	AJAX(url, 'POST', '', function (response) {
281		classRef.createdWidgetID = response.WidgetID;
282	}, false);
283};
284
285widgetNS.FileDroppableClass.prototype.ExecutePostWidgetCreationProcess = function () {
286	var classRef = this;
287	//Make a call again to Step2_CreateDocument passing in the newly created widgetID this time.
288	AJAX('/Admin/DocumentCenter/FileDrop/Step2_CreateDocument_WidgetAdded', 'POST', {
289		contentCollectionID: window.ContentCollectionInstance.ContentCollectionID,
290		widgetID: classRef.createdWidgetID,
291		widgetType: classRef.widgetType
292	}, function (response) {
293		if (response.DocumentIDCollection != 0)
294			classRef.documentIDCollection += response.DocumentIDCollection + ',';
295		AJAX('/Admin/DocumentCenter/FileDrop/Step3_SaveLayoutWidgetOptions', 'POST', {
296			contentCollectionID: window.ContentCollectionInstance.ContentCollectionID,
297			widgetID: classRef.createdWidgetID,
298			widgetType: classRef.widgetType,
299			fileDropSlideshowType: widgetNS.fileDropSlideshowType,
300			documentIDs: classRef.documentIDCollection,
301			slideshowID: classRef.$element.find('.hdnSlideshowUniqueID').val()
302		}, function (response) {
303			ajaxPostBackEnd();
304			//Reset global variables.
305			classRef.ResetGlobalVariables();
306			var contentContainerID = classRef.$element.data('contentcontainerid');
307			window.ContentCollectionInstance.CurrentContentContainerIDs.push(contentContainerID);
308			window.ContentCollectionInstance.reloadContent(function () { $(window.Pages).trigger('ContentReload', classRef.widgetType) });
309			window.setTimeout(makeGridTargetsFileDroppable, 1000);
310			$(window.Pages).trigger('WidgetAdded', classRef.widgetType);
311		}, false);
312	}, false);
313};
314
315widgetNS.FileDroppableClass.prototype.ExecutePostFilesUploadProcess = function () {
316	var classRef = this;
317	// runs after all files have been uploaded or otherwise dealt with
318	AJAX('/Admin/DocumentCenter/FileDrop/Step3_SaveLayoutWidgetOptions', 'POST', {
319		contentCollectionID: window.ContentCollectionInstance.ContentCollectionID,
320		widgetID: classRef.widgetID,
321		widgetType: classRef.widgetType,
322		fileDropSlideshowType: widgetNS.fileDropSlideshowType,
323		documentIDs: classRef.documentIDCollection,
324		slideshowID: classRef.$element.find('.hdnSlideshowUniqueID').val()
325	}, function (response) {
326		//Call to render
327		AJAX('/Admin/DocumentCenter/FileDrop/Step4_Callback', 'POST', {
328			contentCollectionID: window.ContentCollectionInstance.ContentCollectionID,
329			widgetID: classRef.widgetID,
330			widgetType: classRef.widgetType,
331			contentContainerID: classRef.$element.parents('.pageContent').filter(":first").data('containerid'),
332			pageID: window.ContentCollectionInstance.PageID
333		}, function (response) {
334			//Reset global variables.
335			classRef.ResetGlobalVariables();
336			//remove background style
337			classRef.DropHoverEffect();
338			if ($('#divPermissions').length > 0)
339				$('#divPermissions').remove();
340			// response is the data you got back from server in JSON format.
341			classRef.callback(classRef.widgetID, response);
342			ajaxPostBackEnd();
343		}, false);
344	}, false);
345};
346
347widgetNS.FileDroppableClass.prototype.ResetGlobalVariables = function () {
348	var classRef = this;
349	//Reset global variables after files have been uploaded.
350	widgetNS.fileDropSlideshowType = 0;
351	classRef.documentIDCollection = '';
352	classRef.DisableDroppableGridTargets();
353	classRef.createdWidgetID = '';
354};
355
356widgetNS.FileDroppableClass.prototype.ExistingSlideshow = function () {
357	var classRef = this;
358	return classRef.$element.find('.cpSlideshow').length > 0;
359};
360
361/************************************************************************/
362/*************************File Droppable Class Definition Ends*****
363/************************************************************************/
364
365///Updates the text for placeholders for
366///	1 - Slideshow widget
367///	2 - Image widget
368///	3 - RelatedDocuments widget
369///in case the browser version doesnt suppport FileReaderAPI.
370function browserVersionNotCompatibleWithDragDropTextUpdate($element, text) {
371	//Hide element
372	$element.hide();
373
374	var notCompatible = false;
375	var version = browserVersion();
376	
377	if (version != null) {
378		//IE
379		if (isMsie() && parseInt(version) < 10)
380			notCompatible = true;
381		//FF
382		else if (isMozilla() && parseInt(version) < 4)
383			notCompatible = true;
384	}
385	//Chrome
386	else (navigator.userAgent.match(/Chrom(e|ium)\/([0-9]+)\./) !== null && parseInt(navigator.userAgent.match(/Chrom(e|ium)\/([0-9]+)\./)[2]) < 7)
387		notcompatible = true;
388
389	//If the browser does not support Html5 FileReaderAPI, change the placeholder text for the element accordingly.
390	if (notCompatible)	
391		$element.text(text);
392
393	//Show the element
394	$element.show();
395}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.