123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492 |
- // Ensure the $ alias is owned by jQuery.
- (function($) {
- Drupal.settings.Panels = Drupal.settings.Panels || {};
- Drupal.PanelsIPE = {
- editors: {},
- bindClickDelete: function(context) {
- $('a.pane-delete:not(.pane-delete-processed)', context)
- .addClass('pane-delete-processed')
- .click(function() {
- if (confirm(Drupal.t('Remove this pane?'))) {
- $(this).parents('div.panels-ipe-portlet-wrapper').fadeOut('medium', function() {
- var $sortable = $(this).closest('.ui-sortable');
- $(this).empty().remove();
- $sortable.trigger('sortremove');
- });
- $(this).parents('div.panels-ipe-display-container').addClass('changed');
- }
- return false;
- });
- }
- }
- Drupal.behaviors.PanelsIPE = {
- attach: function(context) {
- // Remove any old editors.
- for (var i in Drupal.PanelsIPE.editors) {
- if (Drupal.settings.PanelsIPECacheKeys.indexOf(i) === -1) {
- // Clean-up a little bit and remove it.
- Drupal.PanelsIPE.editors[i].editing = false;
- Drupal.PanelsIPE.editors[i].changed = false;
- delete Drupal.PanelsIPE.editors[i];
- }
- }
- // Initialize new editors.
- for (var i in Drupal.settings.PanelsIPECacheKeys) {
- var key = Drupal.settings.PanelsIPECacheKeys[i];
- $('div#panels-ipe-display-' + key + ':not(.panels-ipe-processed)')
- .addClass('panels-ipe-processed')
- .each(function() {
- // If we're replacing an old IPE, clean it up a little.
- if (Drupal.PanelsIPE.editors[key]) {
- Drupal.PanelsIPE.editors[key].editing = false;
- }
- Drupal.PanelsIPE.editors[key] = new DrupalPanelsIPE(key);
- Drupal.PanelsIPE.editors[key].showContainer();
- });
- }
- $('.panels-ipe-hide-bar').once('panels-ipe-hide-bar-processed').click(function() {
- Drupal.PanelsIPE.editors[key].hideContainer();
- });
- Drupal.PanelsIPE.bindClickDelete(context);
- }
- };
- /**
- * Base object (class) definition for the Panels In-Place Editor.
- *
- * A new instance of this object is instanciated for every unique IPE on a given
- * page.
- *
- * Note that this form is provisional, and we hope to replace it with a more
- * flexible, loosely-coupled model that utilizes separate controllers for the
- * discrete IPE elements. This will result in greater IPE flexibility.
- */
- function DrupalPanelsIPE(cache_key, cfg) {
- cfg = cfg || {};
- var ipe = this;
- this.key = cache_key;
- this.lockPath = null;
- this.state = {};
- this.container = $('#panels-ipe-control-container');
- this.control = $('div#panels-ipe-control-' + cache_key);
- this.initButton = $('div.panels-ipe-startedit', this.control);
- this.cfg = cfg;
- this.changed = false;
- this.sortableOptions = $.extend({
- opacity: 0.75, // opacity of sortable while sorting
- items: 'div.panels-ipe-portlet-wrapper',
- handle: 'div.panels-ipe-draghandle',
- cancel: '.panels-ipe-nodrag',
- dropOnEmpty: true
- }, cfg.sortableOptions || {});
- this.regions = [];
- this.sortables = {};
- $(document).bind('CToolsDetachBehaviors', function() {
- // If the IPE is off and the container is not visible, then we need
- // to reshow the container on modal close.
- if (!$('.panels-ipe-form-container', ipe.control).html() && !ipe.container.is(':visible')) {
- ipe.showContainer();
- ipe.cancelLock();
- }
- // If the IPE is on and we've hidden the bar for a modal, we need to
- // re-display it.
- if (ipe.topParent && ipe.topParent.hasClass('panels-ipe-editing') && ipe.container.is(':not(visible)')) {
- ipe.showContainer();
- }
- });
- // If a user navigates away from a locked IPE, cancel the lock in the background.
- $(window).bind('beforeunload', function() {
- if (!ipe.editing) {
- return;
- }
- if (ipe.topParent && ipe.topParent.hasClass('changed')) {
- ipe.changed = true;
- }
- if (ipe.changed) {
- return Drupal.t('This will discard all unsaved changes. Are you sure?');
- }
- });
- // If a user navigates away from a locked IPE, cancel the lock in the background.
- $(window).bind('unload', function() {
- ipe.cancelLock(true);
- });
- /**
- * If something caused us to abort what we were doing, send a background
- * cancel lock request to the server so that we do not leave stale locks
- * hanging around.
- */
- this.cancelLock = function(sync) {
- // If there's a lockpath and an ajax available, inform server to clear lock.
- // We borrow the ajax options from the customize this page link.
- if (ipe.lockPath && Drupal.ajax['panels-ipe-customize-page']) {
- var ajaxOptions = {
- type: 'POST',
- url: ipe.lockPath
- }
- if (sync) {
- ajaxOptions.async = false;
- }
- // Make sure we don't somehow get another one:
- ipe.lockPath = null;
- // Send the request. This is synchronous to prevent being cancelled.
- $.ajax(ajaxOptions);
- }
- }
- this.activateSortable = function(event, ui) {
- if (!Drupal.settings.Panels || !Drupal.settings.Panels.RegionLock) {
- // don't bother if there are no region locks in play.
- return;
- }
- var region = event.data.region;
- var paneId = ui.item.attr('id').replace('panels-ipe-paneid-', '');
- var disabledRegions = false;
- // Determined if this pane is locked out of this region.
- if (!Drupal.settings.Panels.RegionLock[paneId] || Drupal.settings.Panels.RegionLock[paneId][region]) {
- ipe.sortables[region].sortable('enable');
- ipe.sortables[region].sortable('refresh');
- }
- else {
- disabledRegions = true;
- ipe.sortables[region].sortable('disable');
- ipe.sortables[region].sortable('refresh');
- }
- // If we disabled regions, we need to
- if (disabledRegions) {
- $(event.srcElement).bind('dragstop', function(event, ui) {
- // Go through
- });
- }
- };
- // When dragging is stopped, we need to ensure all sortable regions are enabled.
- this.enableRegions = function(event, ui) {
- for (var i in ipe.regions) {
- ipe.sortables[ipe.regions[i]].sortable('enable');
- ipe.sortables[ipe.regions[i]].sortable('refresh');
- }
- }
- this.initSorting = function() {
- var $region = $(this).parents('.panels-ipe-region');
- var region = $region.attr('id').replace('panels-ipe-regionid-', '');
- ipe.sortables[region] = $(this).sortable(ipe.sortableOptions);
- ipe.regions.push(region);
- $(this).bind('sortactivate', {region: region}, ipe.activateSortable);
- };
- this.initEditing = function(formdata) {
- ipe.editing = true;
- ipe.topParent = $('div#panels-ipe-display-' + cache_key);
- ipe.backup = this.topParent.clone();
- // See http://jqueryui.com/demos/sortable/ for details on the configuration
- // parameters used here.
- ipe.changed = false;
- $('div.panels-ipe-sort-container', ipe.topParent).each(ipe.initSorting);
- // Since the connectWith option only does a one-way hookup, iterate over
- // all sortable regions to connect them with one another.
- $('div.panels-ipe-sort-container', ipe.topParent)
- .sortable('option', 'connectWith', ['div.panels-ipe-sort-container']);
- $('div.panels-ipe-sort-container', ipe.topParent).bind('sortupdate', function() {
- ipe.changed = true;
- });
- $('div.panels-ipe-sort-container', ipe.topParent).bind('sortstop', this.enableRegions);
- // Refresh the control jQuery object.
- ipe.control = $(ipe.control.selector);
- $('.panels-ipe-form-container', ipe.control).append(formdata);
- $('input:submit:not(.ajax-processed), button:not(.ajax-processed)', ipe.control).addClass('ajax-processed').each(function() {
- var element_settings = {};
- element_settings.url = $(this.form).attr('action');
- element_settings.setClick = true;
- element_settings.event = 'click';
- element_settings.progress = { 'type': 'throbber' };
- element_settings.ipe_cache_key = cache_key;
- var base = $(this).attr('id');
- Drupal.ajax[ipe.base] = new Drupal.ajax(base, this, element_settings);
- });
- // Perform visual effects in a particular sequence.
- // .show() + .hide() cannot have speeds associated with them, otherwise
- // it clears out inline styles.
- $('.panels-ipe-on').show();
- ipe.showForm();
- $('body').add(ipe.topParent).addClass('panels-ipe-editing');
- };
- this.hideContainer = function() {
- ipe.container.slideUp('fast');
- };
- this.showContainer = function() {
- ipe.container.slideDown('normal');
- };
- this.showButtons = function() {
- $('.panels-ipe-form-container').hide();
- $('.panels-ipe-button-container').show();
- ipe.showContainer();
- };
- this.showForm = function() {
- $('.panels-ipe-button-container').hide();
- $('.panels-ipe-form-container').show();
- ipe.showContainer();
- };
- this.endEditing = function() {
- ipe.editing = false;
- ipe.lockPath = null;
- $('.panels-ipe-form-container').empty();
- // Re-show all the IPE non-editing meta-elements
- $('div.panels-ipe-off').show('fast');
- // Refresh the container and control jQuery objects.
- ipe.container = $(ipe.container.selector);
- ipe.control = $(ipe.control.selector);
- ipe.showButtons();
- // Re-hide all the IPE meta-elements
- $('div.panels-ipe-on').hide();
- $('.panels-ipe-editing').removeClass('panels-ipe-editing');
- $('div.panels-ipe-sort-container.ui-sortable', ipe.topParent).sortable("destroy");
- };
- this.saveEditing = function() {
- $('div.panels-ipe-region', ipe.topParent).each(function() {
- var val = '';
- var region = $(this).attr('id').split('panels-ipe-regionid-')[1];
- $(this).find('div.panels-ipe-portlet-wrapper').each(function() {
- var id = $(this).attr('id').split('panels-ipe-paneid-')[1];
- if (id) {
- if (val) {
- val += ',';
- }
- val += id;
- }
- });
- $('[name="panel[pane][' + region + ']"]', ipe.control).val(val);
- });
- }
- this.cancelIPE = function() {
- ipe.hideContainer();
- ipe.topParent.fadeOut('medium', function() {
- ipe.topParent.replaceWith(ipe.backup.clone());
- ipe.topParent = $('div#panels-ipe-display-' + ipe.key);
- // Processing of these things got lost in the cloning, but the classes remained behind.
- // @todo this isn't ideal but I can't seem to figure out how to keep an unprocessed backup
- // that will later get processed.
- $('.ctools-use-modal-processed', ipe.topParent).removeClass('ctools-use-modal-processed');
- $('.pane-delete-processed', ipe.topParent).removeClass('pane-delete-processed');
- ipe.topParent.fadeIn('medium');
- Drupal.attachBehaviors();
- });
- };
- this.cancelEditing = function() {
- if (ipe.topParent.hasClass('changed')) {
- ipe.changed = true;
- }
- if (!ipe.changed || confirm(Drupal.t('This will discard all unsaved changes. Are you sure?'))) {
- this.cancelIPE();
- return true;
- }
- else {
- // Cancel the submission.
- return false;
- }
- };
- this.createSortContainers = function() {
- $('div.panels-ipe-region', this.topParent).each(function() {
- $(this).children('div.panels-ipe-portlet-marker').parent()
- .wrapInner('<div class="panels-ipe-sort-container" />');
- // Move our gadgets outside of the sort container so that sortables
- // cannot be placed after them.
- $('div.panels-ipe-portlet-static', this).each(function() {
- $(this).prependTo($(this).parent().parent());
- });
- });
- }
- this.createSortContainers();
- };
- $(function() {
- Drupal.ajax.prototype.commands.initIPE = function(ajax, data, status) {
- if (Drupal.PanelsIPE.editors[data.key]) {
- Drupal.PanelsIPE.editors[data.key].initEditing(data.data);
- Drupal.PanelsIPE.editors[data.key].lockPath = data.lockPath;
- }
- Drupal.attachBehaviors();
- };
- Drupal.ajax.prototype.commands.IPEsetLockState = function(ajax, data, status) {
- if (Drupal.PanelsIPE.editors[data.key]) {
- Drupal.PanelsIPE.editors[data.key].lockPath = data.lockPath;
- }
- };
- Drupal.ajax.prototype.commands.addNewPane = function(ajax, data, status) {
- if (Drupal.PanelsIPE.editors[data.key]) {
- Drupal.PanelsIPE.editors[data.key].changed = true;
- }
- };
- Drupal.ajax.prototype.commands.cancelIPE = function(ajax, data, status) {
- if (Drupal.PanelsIPE.editors[data.key]) {
- Drupal.PanelsIPE.editors[data.key].cancelIPE();
- Drupal.PanelsIPE.editors[data.key].endEditing();
- }
- };
- Drupal.ajax.prototype.commands.unlockIPE = function(ajax, data, status) {
- if (confirm(data.message)) {
- var ajaxOptions = ajax.options;
- ajaxOptions.url = data.break_path;
- $.ajax(ajaxOptions);
- }
- else {
- Drupal.PanelsIPE.editors[data.key].endEditing();
- }
- };
- Drupal.ajax.prototype.commands.endIPE = function(ajax, data, status) {
- if (Drupal.PanelsIPE.editors[data.key]) {
- Drupal.PanelsIPE.editors[data.key].endEditing();
- }
- };
- Drupal.ajax.prototype.commands.insertNewPane = function(ajax, data, status) {
- IPEContainerSelector = '#panels-ipe-regionid-' + data.regionId + ' div.panels-ipe-sort-container';
- firstPaneSelector = IPEContainerSelector + ' div.panels-ipe-portlet-wrapper:first';
- // Insert the new pane before the first existing pane in the region, if
- // any.
- if ($(firstPaneSelector).length) {
- insertData = {
- 'method': 'before',
- 'selector': firstPaneSelector,
- 'data': data.renderedPane,
- 'settings': null
- }
- Drupal.ajax.prototype.commands.insert(ajax, insertData, status);
- }
- // Else, insert it as a first child of the container. Doing so might fall
- // outside of the wrapping markup for the style, but it's the best we can
- // do.
- else {
- insertData = {
- 'method': 'prepend',
- 'selector': IPEContainerSelector,
- 'data': data.renderedPane,
- 'settings': null
- }
- Drupal.ajax.prototype.commands.insert(ajax, insertData, status);
- }
- };
- /**
- * Override the eventResponse on ajax.js so we can add a little extra
- * behavior.
- */
- Drupal.ajax.prototype.ipeReplacedEventResponse = Drupal.ajax.prototype.eventResponse;
- Drupal.ajax.prototype.eventResponse = function (element, event) {
- if (element.ipeCancelThis) {
- element.ipeCancelThis = null;
- return false;
- }
- if ($(this.element).attr('id') == 'panels-ipe-cancel') {
- if (!Drupal.PanelsIPE.editors[this.element_settings.ipe_cache_key].cancelEditing()) {
- return false;
- }
- }
- var retval = this.ipeReplacedEventResponse(element, event);
- if (this.ajaxing && this.element_settings.ipe_cache_key) {
- // Move the throbber so that it appears outside our container.
- if (this.progress.element) {
- $(this.progress.element).addClass('ipe-throbber').appendTo($('body'));
- }
- Drupal.PanelsIPE.editors[this.element_settings.ipe_cache_key].hideContainer();
- }
- // @TODO $('#panels-ipe-throbber-backdrop').remove();
- return retval;
- };
- /**
- * Override the eventResponse on ajax.js so we can add a little extra
- * behavior.
- */
- Drupal.ajax.prototype.ipeReplacedError = Drupal.ajax.prototype.error;
- Drupal.ajax.prototype.error = function (response, uri) {
- var retval = this.ipeReplacedError(response, uri);
- if (this.element_settings.ipe_cache_key) {
- Drupal.PanelsIPE.editors[this.element_settings.ipe_cache_key].showContainer();
- }
- };
- Drupal.ajax.prototype.ipeReplacedBeforeSerialize = Drupal.ajax.prototype.beforeSerialize;
- Drupal.ajax.prototype.beforeSerialize = function (element_settings, options) {
- if ($(this.element).hasClass('panels-ipe-save')) {
- Drupal.PanelsIPE.editors[this.element_settings.ipe_cache_key].saveEditing();
- };
- return this.ipeReplacedBeforeSerialize(element_settings, options);
- };
- });
- /**
- * Apply margin to bottom of the page.
- *
- * Note that directly applying marginBottom does not work in IE. To prevent
- * flickering/jumping page content with client-side caching, this is a regular
- * Drupal behavior.
- *
- * @see admin_menu.js via https://drupal.org/project/admin_menu
- */
- Drupal.behaviors.panelsIpeMarginBottom = {
- attach: function () {
- $('body:not(.panels-ipe)').addClass('panels-ipe');
- }
- };
- })(jQuery);
|