(function($){ Drupal.behaviors.contextReactionBlock = {attach: function(context) { $('form.context-editor:not(.context-block-processed)') .addClass('context-block-processed') .each(function() { var id = $(this).attr('id'); Drupal.contextBlockEditor = Drupal.contextBlockEditor || {}; $(this).bind('init.pageEditor', function(event) { Drupal.contextBlockEditor[id] = new DrupalContextBlockEditor($(this)); }); $(this).bind('start.pageEditor', function(event, context) { // Fallback to first context if param is empty. if (!context) { context = $(this).data('defaultContext'); } Drupal.contextBlockEditor[id].editStart($(this), context); }); $(this).bind('end.pageEditor', function(event) { Drupal.contextBlockEditor[id].editFinish(); }); }); // // Admin Form ======================================================= // // ContextBlockForm: Init. $('#context-blockform:not(.processed)').each(function() { $(this).addClass('processed'); Drupal.contextBlockForm = new DrupalContextBlockForm($(this)); Drupal.contextBlockForm.setState(); }); // ContextBlockForm: Attach block removal handlers. // Lives in behaviors as it may be required for attachment to new DOM elements. $('#context-blockform a.remove:not(.processed)').each(function() { $(this).addClass('processed'); $(this).click(function() { $(this).parents('tr').eq(0).remove(); Drupal.contextBlockForm.setState(); return false; }); }); // Conceal Section title, subtitle and class $('div.context-block-browser', context).nextAll('.form-item').hide(); }}; /** * Context block form. Default form for editing context block reactions. */ DrupalContextBlockForm = function(blockForm) { this.state = {}; this.setState = function() { $('table.context-blockform-region', blockForm).each(function() { var region = $(this).attr('id').split('context-blockform-region-')[1]; var blocks = []; $('tr', $(this)).each(function() { var bid = $(this).attr('id'); var weight = $(this).find('select,input').first().val(); blocks.push({'bid' : bid, 'weight' : weight}); }); Drupal.contextBlockForm.state[region] = blocks; }); // Serialize here and set form element value. $('form input.context-blockform-state').val(JSON.stringify(this.state)); // Hide enabled blocks from selector that are used $('table.context-blockform-region tr').each(function() { var bid = Drupal.checkPlain($(this).attr('id')); $('div.context-blockform-selector input[value="'+bid+'"]').parents('div.form-item').eq(0).hide(); }); // Show blocks in selector that are unused $('div.context-blockform-selector input').each(function() { var bid = $(this).val(); if ($('table.context-blockform-region tr#'+bid).size() === 0) { $(this).parents('div.form-item').eq(0).show(); } }); }; // make sure we update the state right before submits, this takes care of an // apparent race condition between saving the state and the weights getting set // by tabledrag $('#ctools-export-ui-edit-item-form').submit(function() { Drupal.contextBlockForm.setState(); }); // Tabledrag // Add additional handlers to update our blocks. $.each(Drupal.settings.tableDrag, function(base) { var table = $('#' + base + ':not(.processed)', blockForm); if (table && table.is('.context-blockform-region')) { table.addClass('processed'); table.bind('mouseup', function(event) { Drupal.contextBlockForm.setState(); return; }); } }); // Add blocks to a region $('td.blocks a', blockForm).each(function() { $(this).click(function() { var region = $(this).attr('href').split('#')[1]; var base = "context-blockform-region-"+ region; var selected = $("div.context-blockform-selector input:checked"); if (selected.size() > 0) { var weight_warn = false; var min_weight_option = -10; var max_weight_option = 10; var max_observed_weight = min_weight_option - 1; $('table#' + base + ' tr').each(function() { var weight_input_val = $(this).find('select,input').first().val(); if (+weight_input_val > +max_observed_weight) { max_observed_weight = weight_input_val; } }); selected.each(function() { // create new block markup var block = document.createElement('tr'); var text = $(this).parents('div.form-item').eq(0).hide().children('label').text(); var select = '
'; $(block).attr('id', $(this).attr('value')).addClass('draggable'); $(block).html(""+ text + "" + select + "X"); // add block item to region //TODO : Fix it so long blocks don't get stuck when added to top regions and dragged towards bottom regions Drupal.tableDrag[base].makeDraggable(block); $('table#'+base).append(block); if ($.cookie('Drupal.tableDrag.showWeight') == 1) { $('table#'+base).find('.tabledrag-hide').css('display', ''); $('table#'+base).find('.tabledrag-handle').css('display', 'none'); } else { $('table#'+base).find('.tabledrag-hide').css('display', 'none'); $('table#'+base).find('.tabledrag-handle').css('display', ''); } Drupal.attachBehaviors($('table#'+base)); Drupal.contextBlockForm.setState(); $(this).removeAttr('checked'); }); if (weight_warn) { alert(Drupal.t('Desired block weight exceeds available weight options, please check weights for blocks before saving.')); } } return false; }); }); }; /** * Context block editor. AHAH editor for live block reaction editing. */ DrupalContextBlockEditor = function(editor) { this.editor = editor; this.state = {}; this.blocks = {}; this.regions = {}; return this; }; DrupalContextBlockEditor.prototype = { initBlocks : function(blocks) { var self = this; this.blocks = blocks; blocks.each(function() { if($(this).hasClass('context-block-empty')) { $(this).removeClass('context-block-hidden'); } $(this).addClass('draggable'); $(this).prepend($('')); $(this).prepend($('').click(function() { $(this).parent ('.block').eq(0).fadeOut('medium', function() { $(this).remove(); self.updateBlocks(); }); return false; })); }); }, initRegions : function(regions) { this.regions = regions; var ref = this; $(regions).not('.context-ui-processed') .each(function(index, el) { $('.context-ui-add-link', el).click(function(e){ ref.showBlockBrowser($(this).parent()); }).addClass('context-ui-processed'); }); $('.context-block-browser').hide(); }, showBlockBrowser : function(region) { var toggled = false; //figure out the id of the context var activeId = $('.context-editing', this.editor).attr('id').replace('-trigger', ''), context = $('#' + activeId)[0]; this.browser = $('.context-block-browser', context).addClass('active'); //add the filter element to the block browser if (!this.browser.has('input.filter').size()) { var parent = $('.block-browser-sidebar .filter', this.browser); var list = $('.blocks', this.browser); new Drupal.Filter (list, false, '.context-block-addable', parent); } //show a dialog for the blocks list this.browser.show().dialog({ modal : true, close : function() { $(this).dialog('destroy'); //reshow all the categories $('.category', this).show(); $(this).hide().appendTo(context).removeClass('active'); }, height: (.8 * $(window).height()), minHeight:400, minWidth:680, width:680 }); //handle showing / hiding block items when a different category is selected $('.context-block-browser-categories', this.browser).change(function(e) { //if no category is selected we want to show all the items if ($(this).val() == 0) { $('.category', self.browser).show(); } else { $('.category', self.browser).hide(); $('.category-' + $(this).val(), self.browser).show(); } }); //if we already have the function for a different context, rebind it so we don't get dupes if(this.addToRegion) { $('.context-block-addable', this.browser).unbind('click.addToRegion') } //protected function for adding a clicked block to a region var self = this; this.addToRegion = function(e){ var ui = { 'item' : $(this).clone(), 'sender' : $(region) }; $(this).parents('.context-block-browser.active').dialog('close'); $(region).after(ui.item); self.addBlock(e, ui, this.editor, activeId.replace('context-editable-', '')); }; $('.context-block-addable', this.browser).bind('click.addToRegion', this.addToRegion); }, // Update UI to match the current block states. updateBlocks : function() { var browser = $('div.context-block-browser'); // For all enabled blocks, mark corresponding addables as having been added. $('.block, .admin-block').each(function() { var bid = $(this).attr('id').split('block-')[1]; // Ugh. }); // For all hidden addables with no corresponding blocks, mark as addable. $('.context-block-item', browser).each(function() { var bid = $(this).attr('id').split('context-block-addable-')[1]; }); // Mark empty regions. $(this.regions).each(function() { if ($('.block:has(a.context-block)', this).size() > 0) { $(this).removeClass('context-block-region-empty'); } else { $(this).addClass('context-block-region-empty'); } }); }, // Live update a region updateRegion : function(event, ui, region, op) { switch (op) { case 'over': $(region).removeClass('context-block-region-empty'); break; case 'out': if ( // jQuery UI 1.8 $('.draggable-placeholder', region).size() === 1 && $('.block:has(a.context-block)', region).size() == 0 ) { $(region).addClass('context-block-region-empty'); } break; } }, // Remove script elements while dragging & dropping. scriptFix : function(event, ui, editor, context) { if ($('script', ui.item)) { var placeholder = $(Drupal.settings.contextBlockEditor.scriptPlaceholder); var label = $('div.handle label', ui.item).text(); placeholder.children('strong').html(label); $('script', ui.item).parent().empty().append(placeholder); } }, // Add a block to a region through an AJAX load of the block contents. addBlock : function(event, ui, editor, context) { var self = this; if (ui.item.is('.context-block-addable')) { var bid = ui.item.attr('id').split('context-block-addable-')[1]; // Construct query params for our AJAX block request. var params = Drupal.settings.contextBlockEditor.params; params.context_block = bid + ',' + context; // Replace item with loading block. //ui.sender.append(ui.item); var blockLoading = $('
'); ui.item.addClass('context-block-added'); ui.item.after(blockLoading); $.getJSON(Drupal.settings.contextBlockEditor.path, params, function(data) { if (data.status) { var newBlock = $(data.block); if ($('script', newBlock)) { $('script', newBlock).remove(); } blockLoading.fadeOut(function() { $(this).replaceWith(newBlock); self.initBlocks(newBlock); self.updateBlocks(); Drupal.attachBehaviors(newBlock); }); } else { blockLoading.fadeOut(function() { $(this).remove(); }); } }); } else if (ui.item.is(':has(a.context-block)')) { self.updateBlocks(); } }, // Update form hidden field with JSON representation of current block visibility states. setState : function() { var self = this; $(this.regions).each(function() { var region = $('.context-block-region', this).attr('id').split('context-block-region-')[1]; var blocks = []; $('a.context-block', $(this)).each(function() { if ($(this).attr('class').indexOf('edit-') != -1) { var bid = $(this).attr('id').split('context-block-')[1]; var context = $(this).attr('class').split('edit-')[1].split(' ')[0]; context = context ? context : 0; var block = {'bid': bid, 'context': context}; blocks.push(block); } }); self.state[region] = blocks; }); // Serialize here and set form element value. $('input.context-block-editor-state', this.editor).val(JSON.stringify(this.state)); }, //Disable text selection. disableTextSelect : function() { if ($.browser.safari) { $('.block:has(a.context-block):not(:has(input,textarea))').css('WebkitUserSelect','none'); } else if ($.browser.mozilla) { $('.block:has(a.context-block):not(:has(input,textarea))').css('MozUserSelect','none'); } else if ($.browser.msie) { $('.block:has(a.context-block):not(:has(input,textarea))').bind('selectstart.contextBlockEditor', function() { return false; }); } else { $(this).bind('mousedown.contextBlockEditor', function() { return false; }); } }, //Enable text selection. enableTextSelect : function() { if ($.browser.safari) { $('*').css('WebkitUserSelect',''); } else if ($.browser.mozilla) { $('*').css('MozUserSelect',''); } else if ($.browser.msie) { $('*').unbind('selectstart.contextBlockEditor'); } else { $(this).unbind('mousedown.contextBlockEditor'); } }, // Start editing. Attach handlers, begin draggable/sortables. editStart : function(editor, context) { var self = this; // This is redundant to the start handler found in context_ui.js. // However it's necessary that we trigger this class addition before // we call .sortable() as the empty regions need to be visible. $(document.body).addClass('context-editing'); this.editor.addClass('context-editing'); this.disableTextSelect(); this.initBlocks($('.block:has(a.context-block.edit-'+context+')')); this.initRegions($('.context-block-region').parent()); this.updateBlocks(); $('a.context_ui_dialog-stop').hide(); $('.editing-context-label').remove(); var label = $('#context-editable-trigger-'+context+' .label').text(); label = Drupal.t('Now editing: @label', {'@label': label}); editor.parent().parent().prepend('
' + label + '
'); // First pass, enable sortables on all regions. $(this.regions).each(function() { var region = $(this); var params = { revert: true, dropOnEmpty: true, placeholder: 'draggable-placeholder', forcePlaceholderSize: true, items: '> *:has(a.context-block.editable)', handle: 'a.context-block-handle', start: function(event, ui) { self.scriptFix(event, ui, editor, context); }, stop: function(event, ui) { self.addBlock(event, ui, editor, context); }, receive: function(event, ui) { self.addBlock(event, ui, editor, context); }, over: function(event, ui) { self.updateRegion(event, ui, region, 'over'); }, out: function(event, ui) { self.updateRegion(event, ui, region, 'out'); }, cursorAt: {left: 300, top: 0} }; region.sortable(params); }); // Second pass, hook up all regions via connectWith to each other. $(this.regions).each(function() { $(this).sortable('option', 'connectWith', ['.ui-sortable']); }); // Terrible, terrible workaround for parentoffset issue in Safari. // The proper fix for this issue has been committed to jQuery UI, but was // not included in the 1.6 release. Therefore, we do a browser agent hack // to ensure that Safari users are covered by the offset fix found here: // http://dev.jqueryui.com/changeset/2073. if ($.ui.version === '1.6' && $.browser.safari) { $.browser.mozilla = true; } }, // Finish editing. Remove handlers. editFinish : function() { this.editor.removeClass('context-editing'); this.enableTextSelect(); $('.editing-context-label').remove(); // Remove UI elements. $(this.blocks).each(function() { $('a.context-block-handle, a.context-block-remove', this).remove(); if($(this).hasClass('context-block-empty')) { $(this).addClass('context-block-hidden'); } $(this).removeClass('draggable'); }); $('a.context_ui_dialog-stop').show(); this.regions.sortable('destroy'); this.setState(); // Unhack the user agent. if ($.ui.version === '1.6' && $.browser.safari) { $.browser.mozilla = false; } } }; //End of DrupalContextBlockEditor prototype })(jQuery);