danielebarchiesi@4: (function($) { danielebarchiesi@4: danielebarchiesi@4: Drupal.admin = Drupal.admin || {}; danielebarchiesi@4: Drupal.admin.behaviors = Drupal.admin.behaviors || {}; danielebarchiesi@4: Drupal.admin.hashes = Drupal.admin.hashes || {}; danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Core behavior for Administration menu. danielebarchiesi@4: * danielebarchiesi@4: * Test whether there is an administration menu is in the output and execute all danielebarchiesi@4: * registered behaviors. danielebarchiesi@4: */ danielebarchiesi@4: Drupal.behaviors.adminMenu = { danielebarchiesi@4: attach: function (context, settings) { danielebarchiesi@4: // Initialize settings. danielebarchiesi@4: settings.admin_menu = $.extend({ danielebarchiesi@4: suppress: false, danielebarchiesi@4: margin_top: false, danielebarchiesi@4: position_fixed: false, danielebarchiesi@4: tweak_modules: false, danielebarchiesi@4: tweak_permissions: false, danielebarchiesi@4: tweak_tabs: false, danielebarchiesi@4: destination: '', danielebarchiesi@4: basePath: settings.basePath, danielebarchiesi@4: hash: 0, danielebarchiesi@4: replacements: {} danielebarchiesi@4: }, settings.admin_menu || {}); danielebarchiesi@4: // Check whether administration menu should be suppressed. danielebarchiesi@4: if (settings.admin_menu.suppress) { danielebarchiesi@4: return; danielebarchiesi@4: } danielebarchiesi@4: var $adminMenu = $('#admin-menu:not(.admin-menu-processed)', context); danielebarchiesi@4: // Client-side caching; if administration menu is not in the output, it is danielebarchiesi@4: // fetched from the server and cached in the browser. danielebarchiesi@4: if (!$adminMenu.length && settings.admin_menu.hash) { danielebarchiesi@4: Drupal.admin.getCache(settings.admin_menu.hash, function (response) { danielebarchiesi@4: if (typeof response == 'string' && response.length > 0) { danielebarchiesi@4: $('body', context).append(response); danielebarchiesi@4: } danielebarchiesi@4: var $adminMenu = $('#admin-menu:not(.admin-menu-processed)', context); danielebarchiesi@4: // Apply our behaviors. danielebarchiesi@4: Drupal.admin.attachBehaviors(context, settings, $adminMenu); danielebarchiesi@4: // Allow resize event handlers to recalculate sizes/positions. danielebarchiesi@4: $(window).triggerHandler('resize'); danielebarchiesi@4: }); danielebarchiesi@4: } danielebarchiesi@4: // If the menu is in the output already, this means there is a new version. danielebarchiesi@4: else { danielebarchiesi@4: // Apply our behaviors. danielebarchiesi@4: Drupal.admin.attachBehaviors(context, settings, $adminMenu); danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: }; danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Collapse fieldsets on Modules page. danielebarchiesi@4: */ danielebarchiesi@4: Drupal.behaviors.adminMenuCollapseModules = { danielebarchiesi@4: attach: function (context, settings) { danielebarchiesi@4: if (settings.admin_menu.tweak_modules) { danielebarchiesi@4: $('#system-modules fieldset:not(.collapsed)', context).addClass('collapsed'); danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: }; danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Collapse modules on Permissions page. danielebarchiesi@4: */ danielebarchiesi@4: Drupal.behaviors.adminMenuCollapsePermissions = { danielebarchiesi@4: attach: function (context, settings) { danielebarchiesi@4: if (settings.admin_menu.tweak_permissions) { danielebarchiesi@4: // Freeze width of first column to prevent jumping. danielebarchiesi@4: $('#permissions th:first', context).css({ width: $('#permissions th:first', context).width() }); danielebarchiesi@4: // Attach click handler. danielebarchiesi@4: $modules = $('#permissions tr:has(td.module)', context).once('admin-menu-tweak-permissions', function () { danielebarchiesi@4: var $module = $(this); danielebarchiesi@4: $module.bind('click.admin-menu', function () { danielebarchiesi@4: // @todo Replace with .nextUntil() in jQuery 1.4. danielebarchiesi@4: $module.nextAll().each(function () { danielebarchiesi@4: var $row = $(this); danielebarchiesi@4: if ($row.is(':has(td.module)')) { danielebarchiesi@4: return false; danielebarchiesi@4: } danielebarchiesi@4: $row.toggleClass('element-hidden'); danielebarchiesi@4: }); danielebarchiesi@4: }); danielebarchiesi@4: }); danielebarchiesi@4: // Collapse all but the targeted permission rows set. danielebarchiesi@4: if (window.location.hash.length) { danielebarchiesi@4: $modules = $modules.not(':has(' + window.location.hash + ')'); danielebarchiesi@4: } danielebarchiesi@4: $modules.trigger('click.admin-menu'); danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: }; danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Apply margin to page. danielebarchiesi@4: * danielebarchiesi@4: * Note that directly applying marginTop does not work in IE. To prevent danielebarchiesi@4: * flickering/jumping page content with client-side caching, this is a regular danielebarchiesi@4: * Drupal behavior. danielebarchiesi@4: */ danielebarchiesi@4: Drupal.behaviors.adminMenuMarginTop = { danielebarchiesi@4: attach: function (context, settings) { danielebarchiesi@4: if (!settings.admin_menu.suppress && settings.admin_menu.margin_top) { danielebarchiesi@4: $('body:not(.admin-menu)', context).addClass('admin-menu'); danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: }; danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Retrieve content from client-side cache. danielebarchiesi@4: * danielebarchiesi@4: * @param hash danielebarchiesi@4: * The md5 hash of the content to retrieve. danielebarchiesi@4: * @param onSuccess danielebarchiesi@4: * A callback function invoked when the cache request was successful. danielebarchiesi@4: */ danielebarchiesi@4: Drupal.admin.getCache = function (hash, onSuccess) { danielebarchiesi@4: if (Drupal.admin.hashes.hash !== undefined) { danielebarchiesi@4: return Drupal.admin.hashes.hash; danielebarchiesi@4: } danielebarchiesi@4: $.ajax({ danielebarchiesi@4: cache: true, danielebarchiesi@4: type: 'GET', danielebarchiesi@4: dataType: 'text', // Prevent auto-evaluation of response. danielebarchiesi@4: global: false, // Do not trigger global AJAX events. danielebarchiesi@4: url: Drupal.settings.admin_menu.basePath.replace(/admin_menu/, 'js/admin_menu/cache/' + hash), danielebarchiesi@4: success: onSuccess, danielebarchiesi@4: complete: function (XMLHttpRequest, status) { danielebarchiesi@4: Drupal.admin.hashes.hash = status; danielebarchiesi@4: } danielebarchiesi@4: }); danielebarchiesi@4: }; danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * TableHeader callback to determine top viewport offset. danielebarchiesi@4: * danielebarchiesi@4: * @see toolbar.js danielebarchiesi@4: */ danielebarchiesi@4: Drupal.admin.height = function() { danielebarchiesi@4: var $adminMenu = $('#admin-menu'); danielebarchiesi@4: var height = $adminMenu.outerHeight(); danielebarchiesi@4: // In IE, Shadow filter adds some extra height, so we need to remove it from danielebarchiesi@4: // the returned height. danielebarchiesi@4: if ($adminMenu.css('filter') && $adminMenu.css('filter').match(/DXImageTransform\.Microsoft\.Shadow/)) { danielebarchiesi@4: height -= $adminMenu.get(0).filters.item("DXImageTransform.Microsoft.Shadow").strength; danielebarchiesi@4: } danielebarchiesi@4: return height; danielebarchiesi@4: }; danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * @defgroup admin_behaviors Administration behaviors. danielebarchiesi@4: * @{ danielebarchiesi@4: */ danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Attach administrative behaviors. danielebarchiesi@4: */ danielebarchiesi@4: Drupal.admin.attachBehaviors = function (context, settings, $adminMenu) { danielebarchiesi@4: if ($adminMenu.length) { danielebarchiesi@4: $adminMenu.addClass('admin-menu-processed'); danielebarchiesi@4: $.each(Drupal.admin.behaviors, function() { danielebarchiesi@4: this(context, settings, $adminMenu); danielebarchiesi@4: }); danielebarchiesi@4: } danielebarchiesi@4: }; danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Apply 'position: fixed'. danielebarchiesi@4: */ danielebarchiesi@4: Drupal.admin.behaviors.positionFixed = function (context, settings, $adminMenu) { danielebarchiesi@4: if (settings.admin_menu.position_fixed) { danielebarchiesi@4: $adminMenu.addClass('admin-menu-position-fixed'); danielebarchiesi@4: $adminMenu.css('position', 'fixed'); danielebarchiesi@4: } danielebarchiesi@4: }; danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Move page tabs into administration menu. danielebarchiesi@4: */ danielebarchiesi@4: Drupal.admin.behaviors.pageTabs = function (context, settings, $adminMenu) { danielebarchiesi@4: if (settings.admin_menu.tweak_tabs) { danielebarchiesi@4: var $tabs = $(context).find('ul.tabs.primary'); danielebarchiesi@4: $adminMenu.find('#admin-menu-wrapper > ul').eq(1) danielebarchiesi@4: .append($tabs.find('li').addClass('admin-menu-tab')); danielebarchiesi@4: $(context).find('ul.tabs.secondary') danielebarchiesi@4: .appendTo('#admin-menu-wrapper > ul > li.admin-menu-tab.active') danielebarchiesi@4: .removeClass('secondary'); danielebarchiesi@4: $tabs.remove(); danielebarchiesi@4: } danielebarchiesi@4: }; danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Perform dynamic replacements in cached menu. danielebarchiesi@4: */ danielebarchiesi@4: Drupal.admin.behaviors.replacements = function (context, settings, $adminMenu) { danielebarchiesi@4: for (var item in settings.admin_menu.replacements) { danielebarchiesi@4: $(item, $adminMenu).html(settings.admin_menu.replacements[item]); danielebarchiesi@4: } danielebarchiesi@4: }; danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Inject destination query strings for current page. danielebarchiesi@4: */ danielebarchiesi@4: Drupal.admin.behaviors.destination = function (context, settings, $adminMenu) { danielebarchiesi@4: if (settings.admin_menu.destination) { danielebarchiesi@4: $('a.admin-menu-destination', $adminMenu).each(function() { danielebarchiesi@4: this.search += (!this.search.length ? '?' : '&') + Drupal.settings.admin_menu.destination; danielebarchiesi@4: }); danielebarchiesi@4: } danielebarchiesi@4: }; danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Apply JavaScript-based hovering behaviors. danielebarchiesi@4: * danielebarchiesi@4: * @todo This has to run last. If another script registers additional behaviors danielebarchiesi@4: * it will not run last. danielebarchiesi@4: */ danielebarchiesi@4: Drupal.admin.behaviors.hover = function (context, settings, $adminMenu) { danielebarchiesi@4: // Hover emulation for IE 6. danielebarchiesi@4: if ($.browser.msie && parseInt(jQuery.browser.version) == 6) { danielebarchiesi@4: $('li', $adminMenu).hover( danielebarchiesi@4: function () { danielebarchiesi@4: $(this).addClass('iehover'); danielebarchiesi@4: }, danielebarchiesi@4: function () { danielebarchiesi@4: $(this).removeClass('iehover'); danielebarchiesi@4: } danielebarchiesi@4: ); danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: // Delayed mouseout. danielebarchiesi@4: $('li.expandable', $adminMenu).hover( danielebarchiesi@4: function () { danielebarchiesi@4: // Stop the timer. danielebarchiesi@4: clearTimeout(this.sfTimer); danielebarchiesi@4: // Display child lists. danielebarchiesi@4: $('> ul', this) danielebarchiesi@4: .css({left: 'auto', display: 'block'}) danielebarchiesi@4: // Immediately hide nephew lists. danielebarchiesi@4: .parent().siblings('li').children('ul').css({left: '-999em', display: 'none'}); danielebarchiesi@4: }, danielebarchiesi@4: function () { danielebarchiesi@4: // Start the timer. danielebarchiesi@4: var uls = $('> ul', this); danielebarchiesi@4: this.sfTimer = setTimeout(function () { danielebarchiesi@4: uls.css({left: '-999em', display: 'none'}); danielebarchiesi@4: }, 400); danielebarchiesi@4: } danielebarchiesi@4: ); danielebarchiesi@4: }; danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Apply the search bar functionality. danielebarchiesi@4: */ danielebarchiesi@4: Drupal.admin.behaviors.search = function (context, settings, $adminMenu) { danielebarchiesi@4: // @todo Add a HTML ID. danielebarchiesi@4: var $input = $('input.admin-menu-search', $adminMenu); danielebarchiesi@4: // Initialize the current search needle. danielebarchiesi@4: var needle = $input.val(); danielebarchiesi@4: // Cache of all links that can be matched in the menu. danielebarchiesi@4: var links; danielebarchiesi@4: // Minimum search needle length. danielebarchiesi@4: var needleMinLength = 2; danielebarchiesi@4: // Append the results container. danielebarchiesi@4: var $results = $('
').insertAfter($input); danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Executes the search upon user input. danielebarchiesi@4: */ danielebarchiesi@4: function keyupHandler() { danielebarchiesi@4: var matches, $html, value = $(this).val(); danielebarchiesi@4: // Only proceed if the search needle has changed. danielebarchiesi@4: if (value !== needle) { danielebarchiesi@4: needle = value; danielebarchiesi@4: // Initialize the cache of menu links upon first search. danielebarchiesi@4: if (!links && needle.length >= needleMinLength) { danielebarchiesi@4: // @todo Limit to links in dropdown menus; i.e., skip menu additions. danielebarchiesi@4: links = buildSearchIndex($adminMenu.find('li:not(.admin-menu-action, .admin-menu-action li) > a')); danielebarchiesi@4: } danielebarchiesi@4: // Empty results container when deleting search text. danielebarchiesi@4: if (needle.length < needleMinLength) { danielebarchiesi@4: $results.empty(); danielebarchiesi@4: } danielebarchiesi@4: // Only search if the needle is long enough. danielebarchiesi@4: if (needle.length >= needleMinLength && links) { danielebarchiesi@4: matches = findMatches(needle, links); danielebarchiesi@4: // Build the list in a detached DOM node. danielebarchiesi@4: $html = buildResultsList(matches); danielebarchiesi@4: // Display results. danielebarchiesi@4: $results.empty().append($html); danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Builds the search index. danielebarchiesi@4: */ danielebarchiesi@4: function buildSearchIndex($links) { danielebarchiesi@4: return $links danielebarchiesi@4: .map(function () { danielebarchiesi@4: var text = (this.textContent || this.innerText); danielebarchiesi@4: // Skip menu entries that do not contain any text (e.g., the icon). danielebarchiesi@4: if (typeof text === 'undefined') { danielebarchiesi@4: return; danielebarchiesi@4: } danielebarchiesi@4: return { danielebarchiesi@4: text: text, danielebarchiesi@4: textMatch: text.toLowerCase(), danielebarchiesi@4: element: this danielebarchiesi@4: }; danielebarchiesi@4: }); danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Searches the index for a given needle and returns matching entries. danielebarchiesi@4: */ danielebarchiesi@4: function findMatches(needle, links) { danielebarchiesi@4: var needleMatch = needle.toLowerCase(); danielebarchiesi@4: // Select matching links from the cache. danielebarchiesi@4: return $.grep(links, function (link) { danielebarchiesi@4: return link.textMatch.indexOf(needleMatch) !== -1; danielebarchiesi@4: }); danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: /** danielebarchiesi@4: * Builds the search result list in a detached DOM node. danielebarchiesi@4: */ danielebarchiesi@4: function buildResultsList(matches) { danielebarchiesi@4: var $html = $(' '); danielebarchiesi@4: $.each(matches, function () { danielebarchiesi@4: var result = this.text; danielebarchiesi@4: var $element = $(this.element); danielebarchiesi@4: danielebarchiesi@4: // Check whether there is a top-level category that can be prepended. danielebarchiesi@4: var $category = $element.closest('#admin-menu-wrapper > ul > li'); danielebarchiesi@4: var categoryText = $category.find('> a').text() danielebarchiesi@4: if ($category.length && categoryText) { danielebarchiesi@4: result = categoryText + ': ' + result; danielebarchiesi@4: } danielebarchiesi@4: danielebarchiesi@4: var $result = $('