comparison sites/all/modules/admin_menu/admin_menu.js @ 4:ce11bbd8f642

added modules
author danieleb <danielebarchiesi@me.com>
date Thu, 19 Sep 2013 10:38:44 +0100
parents
children
comparison
equal deleted inserted replaced
3:b28be78d8160 4:ce11bbd8f642
1 (function($) {
2
3 Drupal.admin = Drupal.admin || {};
4 Drupal.admin.behaviors = Drupal.admin.behaviors || {};
5 Drupal.admin.hashes = Drupal.admin.hashes || {};
6
7 /**
8 * Core behavior for Administration menu.
9 *
10 * Test whether there is an administration menu is in the output and execute all
11 * registered behaviors.
12 */
13 Drupal.behaviors.adminMenu = {
14 attach: function (context, settings) {
15 // Initialize settings.
16 settings.admin_menu = $.extend({
17 suppress: false,
18 margin_top: false,
19 position_fixed: false,
20 tweak_modules: false,
21 tweak_permissions: false,
22 tweak_tabs: false,
23 destination: '',
24 basePath: settings.basePath,
25 hash: 0,
26 replacements: {}
27 }, settings.admin_menu || {});
28 // Check whether administration menu should be suppressed.
29 if (settings.admin_menu.suppress) {
30 return;
31 }
32 var $adminMenu = $('#admin-menu:not(.admin-menu-processed)', context);
33 // Client-side caching; if administration menu is not in the output, it is
34 // fetched from the server and cached in the browser.
35 if (!$adminMenu.length && settings.admin_menu.hash) {
36 Drupal.admin.getCache(settings.admin_menu.hash, function (response) {
37 if (typeof response == 'string' && response.length > 0) {
38 $('body', context).append(response);
39 }
40 var $adminMenu = $('#admin-menu:not(.admin-menu-processed)', context);
41 // Apply our behaviors.
42 Drupal.admin.attachBehaviors(context, settings, $adminMenu);
43 // Allow resize event handlers to recalculate sizes/positions.
44 $(window).triggerHandler('resize');
45 });
46 }
47 // If the menu is in the output already, this means there is a new version.
48 else {
49 // Apply our behaviors.
50 Drupal.admin.attachBehaviors(context, settings, $adminMenu);
51 }
52 }
53 };
54
55 /**
56 * Collapse fieldsets on Modules page.
57 */
58 Drupal.behaviors.adminMenuCollapseModules = {
59 attach: function (context, settings) {
60 if (settings.admin_menu.tweak_modules) {
61 $('#system-modules fieldset:not(.collapsed)', context).addClass('collapsed');
62 }
63 }
64 };
65
66 /**
67 * Collapse modules on Permissions page.
68 */
69 Drupal.behaviors.adminMenuCollapsePermissions = {
70 attach: function (context, settings) {
71 if (settings.admin_menu.tweak_permissions) {
72 // Freeze width of first column to prevent jumping.
73 $('#permissions th:first', context).css({ width: $('#permissions th:first', context).width() });
74 // Attach click handler.
75 $modules = $('#permissions tr:has(td.module)', context).once('admin-menu-tweak-permissions', function () {
76 var $module = $(this);
77 $module.bind('click.admin-menu', function () {
78 // @todo Replace with .nextUntil() in jQuery 1.4.
79 $module.nextAll().each(function () {
80 var $row = $(this);
81 if ($row.is(':has(td.module)')) {
82 return false;
83 }
84 $row.toggleClass('element-hidden');
85 });
86 });
87 });
88 // Collapse all but the targeted permission rows set.
89 if (window.location.hash.length) {
90 $modules = $modules.not(':has(' + window.location.hash + ')');
91 }
92 $modules.trigger('click.admin-menu');
93 }
94 }
95 };
96
97 /**
98 * Apply margin to page.
99 *
100 * Note that directly applying marginTop does not work in IE. To prevent
101 * flickering/jumping page content with client-side caching, this is a regular
102 * Drupal behavior.
103 */
104 Drupal.behaviors.adminMenuMarginTop = {
105 attach: function (context, settings) {
106 if (!settings.admin_menu.suppress && settings.admin_menu.margin_top) {
107 $('body:not(.admin-menu)', context).addClass('admin-menu');
108 }
109 }
110 };
111
112 /**
113 * Retrieve content from client-side cache.
114 *
115 * @param hash
116 * The md5 hash of the content to retrieve.
117 * @param onSuccess
118 * A callback function invoked when the cache request was successful.
119 */
120 Drupal.admin.getCache = function (hash, onSuccess) {
121 if (Drupal.admin.hashes.hash !== undefined) {
122 return Drupal.admin.hashes.hash;
123 }
124 $.ajax({
125 cache: true,
126 type: 'GET',
127 dataType: 'text', // Prevent auto-evaluation of response.
128 global: false, // Do not trigger global AJAX events.
129 url: Drupal.settings.admin_menu.basePath.replace(/admin_menu/, 'js/admin_menu/cache/' + hash),
130 success: onSuccess,
131 complete: function (XMLHttpRequest, status) {
132 Drupal.admin.hashes.hash = status;
133 }
134 });
135 };
136
137 /**
138 * TableHeader callback to determine top viewport offset.
139 *
140 * @see toolbar.js
141 */
142 Drupal.admin.height = function() {
143 var $adminMenu = $('#admin-menu');
144 var height = $adminMenu.outerHeight();
145 // In IE, Shadow filter adds some extra height, so we need to remove it from
146 // the returned height.
147 if ($adminMenu.css('filter') && $adminMenu.css('filter').match(/DXImageTransform\.Microsoft\.Shadow/)) {
148 height -= $adminMenu.get(0).filters.item("DXImageTransform.Microsoft.Shadow").strength;
149 }
150 return height;
151 };
152
153 /**
154 * @defgroup admin_behaviors Administration behaviors.
155 * @{
156 */
157
158 /**
159 * Attach administrative behaviors.
160 */
161 Drupal.admin.attachBehaviors = function (context, settings, $adminMenu) {
162 if ($adminMenu.length) {
163 $adminMenu.addClass('admin-menu-processed');
164 $.each(Drupal.admin.behaviors, function() {
165 this(context, settings, $adminMenu);
166 });
167 }
168 };
169
170 /**
171 * Apply 'position: fixed'.
172 */
173 Drupal.admin.behaviors.positionFixed = function (context, settings, $adminMenu) {
174 if (settings.admin_menu.position_fixed) {
175 $adminMenu.addClass('admin-menu-position-fixed');
176 $adminMenu.css('position', 'fixed');
177 }
178 };
179
180 /**
181 * Move page tabs into administration menu.
182 */
183 Drupal.admin.behaviors.pageTabs = function (context, settings, $adminMenu) {
184 if (settings.admin_menu.tweak_tabs) {
185 var $tabs = $(context).find('ul.tabs.primary');
186 $adminMenu.find('#admin-menu-wrapper > ul').eq(1)
187 .append($tabs.find('li').addClass('admin-menu-tab'));
188 $(context).find('ul.tabs.secondary')
189 .appendTo('#admin-menu-wrapper > ul > li.admin-menu-tab.active')
190 .removeClass('secondary');
191 $tabs.remove();
192 }
193 };
194
195 /**
196 * Perform dynamic replacements in cached menu.
197 */
198 Drupal.admin.behaviors.replacements = function (context, settings, $adminMenu) {
199 for (var item in settings.admin_menu.replacements) {
200 $(item, $adminMenu).html(settings.admin_menu.replacements[item]);
201 }
202 };
203
204 /**
205 * Inject destination query strings for current page.
206 */
207 Drupal.admin.behaviors.destination = function (context, settings, $adminMenu) {
208 if (settings.admin_menu.destination) {
209 $('a.admin-menu-destination', $adminMenu).each(function() {
210 this.search += (!this.search.length ? '?' : '&') + Drupal.settings.admin_menu.destination;
211 });
212 }
213 };
214
215 /**
216 * Apply JavaScript-based hovering behaviors.
217 *
218 * @todo This has to run last. If another script registers additional behaviors
219 * it will not run last.
220 */
221 Drupal.admin.behaviors.hover = function (context, settings, $adminMenu) {
222 // Hover emulation for IE 6.
223 if ($.browser.msie && parseInt(jQuery.browser.version) == 6) {
224 $('li', $adminMenu).hover(
225 function () {
226 $(this).addClass('iehover');
227 },
228 function () {
229 $(this).removeClass('iehover');
230 }
231 );
232 }
233
234 // Delayed mouseout.
235 $('li.expandable', $adminMenu).hover(
236 function () {
237 // Stop the timer.
238 clearTimeout(this.sfTimer);
239 // Display child lists.
240 $('> ul', this)
241 .css({left: 'auto', display: 'block'})
242 // Immediately hide nephew lists.
243 .parent().siblings('li').children('ul').css({left: '-999em', display: 'none'});
244 },
245 function () {
246 // Start the timer.
247 var uls = $('> ul', this);
248 this.sfTimer = setTimeout(function () {
249 uls.css({left: '-999em', display: 'none'});
250 }, 400);
251 }
252 );
253 };
254
255 /**
256 * Apply the search bar functionality.
257 */
258 Drupal.admin.behaviors.search = function (context, settings, $adminMenu) {
259 // @todo Add a HTML ID.
260 var $input = $('input.admin-menu-search', $adminMenu);
261 // Initialize the current search needle.
262 var needle = $input.val();
263 // Cache of all links that can be matched in the menu.
264 var links;
265 // Minimum search needle length.
266 var needleMinLength = 2;
267 // Append the results container.
268 var $results = $('<div />').insertAfter($input);
269
270 /**
271 * Executes the search upon user input.
272 */
273 function keyupHandler() {
274 var matches, $html, value = $(this).val();
275 // Only proceed if the search needle has changed.
276 if (value !== needle) {
277 needle = value;
278 // Initialize the cache of menu links upon first search.
279 if (!links && needle.length >= needleMinLength) {
280 // @todo Limit to links in dropdown menus; i.e., skip menu additions.
281 links = buildSearchIndex($adminMenu.find('li:not(.admin-menu-action, .admin-menu-action li) > a'));
282 }
283 // Empty results container when deleting search text.
284 if (needle.length < needleMinLength) {
285 $results.empty();
286 }
287 // Only search if the needle is long enough.
288 if (needle.length >= needleMinLength && links) {
289 matches = findMatches(needle, links);
290 // Build the list in a detached DOM node.
291 $html = buildResultsList(matches);
292 // Display results.
293 $results.empty().append($html);
294 }
295 }
296 }
297
298 /**
299 * Builds the search index.
300 */
301 function buildSearchIndex($links) {
302 return $links
303 .map(function () {
304 var text = (this.textContent || this.innerText);
305 // Skip menu entries that do not contain any text (e.g., the icon).
306 if (typeof text === 'undefined') {
307 return;
308 }
309 return {
310 text: text,
311 textMatch: text.toLowerCase(),
312 element: this
313 };
314 });
315 }
316
317 /**
318 * Searches the index for a given needle and returns matching entries.
319 */
320 function findMatches(needle, links) {
321 var needleMatch = needle.toLowerCase();
322 // Select matching links from the cache.
323 return $.grep(links, function (link) {
324 return link.textMatch.indexOf(needleMatch) !== -1;
325 });
326 }
327
328 /**
329 * Builds the search result list in a detached DOM node.
330 */
331 function buildResultsList(matches) {
332 var $html = $('<ul class="dropdown admin-menu-search-results" />');
333 $.each(matches, function () {
334 var result = this.text;
335 var $element = $(this.element);
336
337 // Check whether there is a top-level category that can be prepended.
338 var $category = $element.closest('#admin-menu-wrapper > ul > li');
339 var categoryText = $category.find('> a').text()
340 if ($category.length && categoryText) {
341 result = categoryText + ': ' + result;
342 }
343
344 var $result = $('<li><a href="' + $element.attr('href') + '">' + result + '</a></li>');
345 $result.data('original-link', $(this.element).parent());
346 $html.append($result);
347 });
348 return $html;
349 }
350
351 /**
352 * Highlights selected result.
353 */
354 function resultsHandler(e) {
355 var $this = $(this);
356 var show = e.type === 'mouseenter' || e.type === 'focusin';
357 $this.trigger(show ? 'showPath' : 'hidePath', [this]);
358 }
359
360 /**
361 * Closes the search results and clears the search input.
362 */
363 function resultsClickHandler(e, link) {
364 var $original = $(this).data('original-link');
365 $original.trigger('mouseleave');
366 $input.val('').trigger('keyup');
367 }
368
369 /**
370 * Shows the link in the menu that corresponds to a search result.
371 */
372 function highlightPathHandler(e, link) {
373 if (link) {
374 var $original = $(link).data('original-link');
375 var show = e.type === 'showPath';
376 // Toggle an additional CSS class to visually highlight the matching link.
377 // @todo Consider using same visual appearance as regular hover.
378 $original.toggleClass('highlight', show);
379 $original.trigger(show ? 'mouseenter' : 'mouseleave');
380 }
381 }
382
383 // Attach showPath/hidePath handler to search result entries.
384 $results.delegate('li', 'mouseenter mouseleave focus blur', resultsHandler);
385 // Hide the result list after a link has been clicked, useful for overlay.
386 $results.delegate('li', 'click', resultsClickHandler);
387 // Attach hover/active highlight behavior to search result entries.
388 $adminMenu.delegate('.admin-menu-search-results li', 'showPath hidePath', highlightPathHandler);
389 // Attach the search input event handler.
390 $input.bind('keyup search', keyupHandler);
391 };
392
393 /**
394 * @} End of "defgroup admin_behaviors".
395 */
396
397 })(jQuery);