b24ac4b3eb4c8af092a800cc42be5ab52f804d18
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Resources / Public / JavaScript / ClickMenu.js
1 /*
2 * This file is part of the TYPO3 CMS project.
3 *
4 * It is free software; you can redistribute it and/or modify it under
5 * the terms of the GNU General Public License, either version 2
6 * of the License, or any later version.
7 *
8 * For the full copyright and license information, please read the
9 * LICENSE.txt file that was distributed with this source code.
10 *
11 * The TYPO3 project - inspiring people to share!
12 */
13
14 /**
15 * Module: TYPO3/CMS/Backend/ClickMenu
16 * Javascript container used to load the clickmenu via AJAX
17 * to render the result in a layer next to the mouse cursor
18 */
19 define(['jquery'], function($) {
20
21 /**
22 *
23 * @type {{mousePos: {X: null, Y: null}, delayClickMenuHide: boolean}}
24 * @exports TYPO3/CMS/Backend/ClickMenu
25 */
26 var ClickMenu = {
27 mousePos: {
28 X: null,
29 Y: null
30 },
31 delayClickMenuHide: false
32 };
33
34 /**
35 * Initialize events
36 */
37 ClickMenu.initializeEvents = function() {
38 $(document).on('click contextmenu', '.t3-js-clickmenutrigger', function(event) {
39 // if there is an other "inline" onclick setting, clickmenu is not triggered
40 // usually this is the case for the foldertree
41 if ($(this).prop('onclick') && event.type === 'click') {
42 return;
43 }
44 event.preventDefault();
45 ClickMenu.show(
46 $(this).data('table'),
47 $(this).data('uid'),
48 $(this).data('listframe'),
49 $(this).data('iteminfo'),
50 $(this).data('parameters')
51 );
52 });
53
54 // register mouse movement inside the document
55 $(document).on('mousemove', ClickMenu.storeMousePositionEvent);
56 };
57
58 /**
59 * Main function, called from most clickmenu links
60 *
61 * @param {String} table Table from where info should be fetched
62 * @param {(String|Number)} uid The UID of the item
63 * @param {String} listFr list Frame?
64 * @param {String} enDisItems Items to disable / enable
65 * @param {String} addParams Additional params
66 * @return void
67 */
68 ClickMenu.show = function(table, uid, listFr, enDisItems, addParams) {
69 var parameters = '';
70
71 if (typeof table !== 'undefined') {
72 parameters += 'table=' + encodeURIComponent(table);
73 }
74 if (typeof uid !== 'undefined') {
75 parameters += (parameters.length > 0 ? '&' : '') + 'uid=' + uid;
76 }
77 if (typeof listFr !== 'undefined') {
78 parameters += (parameters.length > 0 ? '&' : '') + 'listFr=' + listFr;
79 }
80 if (typeof enDisItems !== 'undefined') {
81 parameters += (parameters.length > 0 ? '&' : '') + 'enDisItems=' + enDisItems;
82 }
83 if (typeof addParams !== 'undefined') {
84 parameters += (parameters.length > 0 ? '&' : '') + 'addParams=' + addParams;
85 }
86 this.fetch(parameters);
87 };
88
89 /**
90 * Make the AJAX request
91 *
92 * @param {array} parameters Parameters sent to the server
93 * @return void
94 */
95 ClickMenu.fetch = function(parameters) {
96 var url = TYPO3.settings.ajaxUrls['contextmenu'];
97 if (parameters) {
98 url += ((url.indexOf('?') == -1) ? '?' : '&') + parameters;
99 }
100 $.ajax(url).done(function(response) {
101 // No response content, usually because a copy/paste action was triggered and just the frame needs
102 // to be reloaded.
103 if (typeof response.items === "undefined") {
104 var res = parameters.match(/&reloadListFrame=(0|1|2)(&|$)/);
105 if (parseInt(res[1], 0)) {
106 top.content.list_frame.location.reload(true);
107 }
108 } else {
109 ClickMenu.populateData(response.items, response.level);
110 }
111 });
112 };
113
114 /**
115 * fills the clickmenu with content and displays it correctly
116 * depending on the mouse position
117 *
118 * @param {array} items The data that will be put in the menu
119 * @param {Number} level The depth of the clickmenu
120 */
121 ClickMenu.populateData = function(items, level) {
122 this.initializeClickMenuContainer();
123
124 level = parseInt(level, 10) || 0;
125 var $obj = $('#contentMenu' + level);
126
127 if ($obj.length && (level === 0 || $('#contentMenu' + (level-1)).is(':visible'))) {
128 $obj.html('<div class="list-group">' + items.join('') + '</div>');
129 var x = this.mousePos.X;
130 var y = this.mousePos.Y;
131 var dimsWindow = {
132 width: $(document).width()-20, // saving margin for scrollbars
133 height: $(document).height()
134 };
135
136 // dimensions for the clickmenu
137 var dims = {
138 width: $obj.width(),
139 height: $obj.height()
140 };
141
142 var relative = {
143 X: this.mousePos.X - $(document).scrollLeft(),
144 Y: this.mousePos.Y - $(document).scrollTop()
145 };
146
147 // adjusting the Y position of the layer to fit it into the window frame
148 // if there is enough space above then put it upwards,
149 // otherwise adjust it to the bottom of the window
150 if (dimsWindow.height - dims.height < relative.Y) {
151 if (relative.Y > dims.height) {
152 y -= (dims.height - 10);
153 } else {
154 y += (dimsWindow.height - dims.height - relative.Y);
155 }
156 }
157 // adjusting the X position like Y above, but align it to the left side of the viewport if it does not fit completely
158 if (dimsWindow.width - dims.width < relative.X) {
159 if (relative.X > dims.width) {
160 x -= (dims.width - 10);
161 } else if ((dimsWindow.width - dims.width - relative.X) < $(document).scrollLeft()) {
162 x = $(document).scrollLeft();
163 } else {
164 x += (dimsWindow.width - dims.width - relative.X);
165 }
166 }
167
168 $obj.css({left: x + 'px', top: y + 'px'}).show();
169 }
170 };
171
172 /**
173 * event handler function that saves the
174 * actual position of the mouse
175 * in the Clickmenu object
176 *
177 * @param {Event} event The event object
178 */
179 ClickMenu.storeMousePositionEvent = function(event) {
180 ClickMenu.mousePos.X = event.pageX;
181 ClickMenu.mousePos.Y = event.pageY;
182 ClickMenu.mouseOutFromMenu('#contentMenu0');
183 ClickMenu.mouseOutFromMenu('#contentMenu1');
184 };
185
186 /**
187 * hides a visible menu if the mouse has moved outside
188 * of the object
189 *
190 * @param {Object} obj The object to hide
191 */
192 ClickMenu.mouseOutFromMenu = function(obj) {
193 var $element = $(obj);
194
195 if ($element.length > 0 && $element.is(':visible') && !this.within($element, this.mousePos.X, this.mousePos.Y)) {
196 this.hide($element);
197 } else if ($element.length > 0 && $element.is(':visible')) {
198 this.delayClickMenuHide = true;
199 }
200 };
201
202 /**
203 *
204 * @param {Object} $element
205 * @param {Number} x
206 * @param {Number} y
207 * @returns {Boolean}
208 */
209 ClickMenu.within = function($element, x, y) {
210 var offset = $element.offset();
211 return (y >= offset.top &&
212 y < offset.top + $element.height() &&
213 x >= offset.left &&
214 x < offset.left + $element.width());
215 };
216
217 /**
218 * hides a clickmenu
219 *
220 * @param {Object} obj The clickmenu object to hide
221 */
222 ClickMenu.hide = function(obj) {
223 this.delayClickMenuHide = false;
224 window.setTimeout(function() {
225 if (!ClickMenu.delayClickMenuHide) {
226 $(obj).hide();
227 }
228 }, 500);
229 };
230
231 /**
232 * hides all clickmenus
233 */
234 ClickMenu.hideAll = function() {
235 this.hide('#contentMenu0');
236 this.hide('#contentMenu1');
237 };
238
239 /**
240 * manipulates the DOM to add the divs needed for clickmenu at the bottom of the <body>-tag
241 */
242 ClickMenu.initializeClickMenuContainer = function() {
243 if ($('#contentMenu0').length === 0) {
244 var code = '<div id="contentMenu0" class="context-menu"></div><div id="contentMenu1" class="context-menu" style="display: block;"></div>';
245 $('body').append(code);
246 }
247 };
248
249 ClickMenu.initializeEvents();
250
251 // make it globally available
252 TYPO3.ClickMenu = ClickMenu;
253 return ClickMenu;
254 });