Permalink
Browse files

fix(js): deprecate elgg.ui.widgets more reliably

Widgets JS API has been moved to a named elgg/widgets AMD module,
which is inlined in elgg.js to make sure the API is available at runtime.
This allows us to deprecate elgg.ui.widgets in a manner that is less likely
to break plugin code, i.e. ensuring that the API is available when elgg AMD
module is loaded

Fixes #9523
  • Loading branch information...
hypeJunction committed Mar 23, 2016
1 parent ca20aae commit c25c5211c307fc0b4c869f3e098c5002494d77cf
Showing with 262 additions and 224 deletions.
  1. +1 −0 docs/guides/upgrading.rst
  2. +35 −215 js/lib/ui.widgets.js
  3. +7 −0 views/default/elgg.js.php
  4. +219 −9 views/default/elgg/widgets.js
@@ -20,6 +20,7 @@ Deprecated APIs
* ``set_default_filestore``
* ``elgg_get_config('siteemail')``: Use ``elgg_get_site_entity()->email``
* URLs starting with ``/css/`` and ``/js/``: ``Use elgg_get_simplecache_url()``
* ``elgg.ui.widgets`` JavaScript object is deprecated by ``elgg/widgets`` AMD module
Added ``elgg/widgets`` module
-----------------------------
View
@@ -1,223 +1,43 @@
elgg.provide('elgg.ui.widgets');
/**
* Widgets initialization
*
* @return void
* @requires jqueryui.sortable
*/
elgg.ui.widgets.init = function() {
// the AMD version of init sets this to avoid the deprecation warning
if (!elgg.ui.widgets.init._amd) {
elgg.deprecated_notice('Don\'t use elgg.ui.widgets directly. Use the AMD elgg/widgets module', '2.1');
}
delete elgg.ui.widgets.init._amd;
// widget layout?
if ($(".elgg-widgets").length === 0) {
return;
}
$(".elgg-widgets").sortable({
items: 'div.elgg-module-widget.elgg-state-draggable',
connectWith: '.elgg-widgets',
handle: '.elgg-widget-handle',
forcePlaceholderSize: true,
placeholder: 'elgg-widget-placeholder',
opacity: 0.8,
revert: 500,
stop: elgg.ui.widgets.move
});
$('.elgg-widgets-add-panel li.elgg-state-available').click(elgg.ui.widgets.add);
$(document).on('click', 'a.elgg-widget-delete-button', elgg.ui.widgets.remove);
$(document).on('submit', '.elgg-widget-edit > form ', elgg.ui.widgets.saveSettings);
$(document).on('click', 'a.elgg-widget-collapse-button', elgg.ui.widgets.collapseToggle);
elgg.ui.widgets.setMinHeight(".elgg-widgets");
};
/**
* Adds a new widget
*
* Makes Ajax call to persist new widget and inserts the widget html
*
* @param {Object} event
* @return void
* Even thought this looks like an async require call, it in fact does not
* issue an async call to load elgg/widgets, which is inlined in elgg.js.
* This makes sure elgg.ui.widgets methods are available for plugins to use,
* once elgg module is loaded.
* @deprecated 2.1
*/
elgg.ui.widgets.add = function(event) {
var type = $(this).data('elgg-widget-type');
require(['elgg', 'elgg/widgets'], function (elgg, widgets) {
// if multiple instances not allow, disable this widget type add button
var multiple = $(this).attr('class').indexOf('elgg-widget-multiple') != -1;
if (multiple === false) {
$(this).addClass('elgg-state-unavailable');
$(this).removeClass('elgg-state-available');
$(this).unbind('click', elgg.ui.widgets.add);
}
elgg.provide('elgg.ui.widgets');
elgg.action('widgets/add', {
data: {
handler: type,
page_owner_guid: elgg.get_page_owner_guid(),
context: $("input[name='widget_context']").val(),
show_access: $("input[name='show_access']").val(),
default_widgets: $("input[name='default_widgets']").val() || 0
elgg.ui.widgets = {
_notice: function() {
elgg.deprecated_notice('Don\'t use elgg.ui.widgets directly. Use the AMD elgg/widgets module', '2.1');
},
success: function(json) {
$('#elgg-widget-col-1').prepend(json.output);
}
});
event.preventDefault();
};
/**
* Persist the widget's new position
*
* @param {Object} event
* @param {Object} ui
*
* @return void
*/
elgg.ui.widgets.move = function(event, ui) {
// elgg-widget-<guid>
var guidString = ui.item.attr('id');
guidString = guidString.substr(guidString.indexOf('elgg-widget-') + "elgg-widget-".length);
// elgg-widget-col-<column>
var col = ui.item.parent().attr('id');
col = col.substr(col.indexOf('elgg-widget-col-') + "elgg-widget-col-".length);
elgg.action('widgets/move', {
data: {
widget_guid: guidString,
column: col,
position: ui.item.index()
}
});
// @hack fixes jquery-ui/opera bug where draggable elements jump
ui.item.css('top', 0);
ui.item.css('left', 0);
};
/**
* Removes a widget from the layout
*
* Event callback the uses Ajax to delete the widget and removes its HTML
*
* @param {Object} event
* @return void
*/
elgg.ui.widgets.remove = function(event) {
if (confirm(elgg.echo('deleteconfirm')) === false) {
event.preventDefault();
return;
}
var $widget = $(this).closest('.elgg-module-widget');
// if widget type is single instance type, enable the add buton
var type = $(this).data('elgg-widget-type');
$container = $(this).parents('.elgg-layout-widgets').first();
$button = $('[data-elgg-widget-type="' + type + '"]', $container);
var multiple = $button.attr('class').indexOf('elgg-widget-multiple') != -1;
if (multiple === false) {
$button.addClass('elgg-state-available');
$button.removeClass('elgg-state-unavailable');
$button.unbind('click', elgg.ui.widgets.add); // make sure we don't bind twice
$button.click(elgg.ui.widgets.add);
}
$widget.remove();
// delete the widget through ajax
elgg.action($(this).attr('href'));
event.preventDefault();
};
/**
* Toggle the collapse state of the widget
*
* @param {Object} event
* @return void
*/
elgg.ui.widgets.collapseToggle = function(event) {
$(this).toggleClass('elgg-widget-collapsed');
$(this).parent().parent().find('.elgg-body').slideToggle('medium');
event.preventDefault();
};
/**
* Save a widget's settings
*
* Uses Ajax to save the settings and updates the HTML.
*
* @param {Object} event
* @return void
*/
elgg.ui.widgets.saveSettings = function(event) {
$(this).parent().slideToggle('medium');
var $widgetContent = $(this).parent().parent().children('.elgg-widget-content');
// stick the ajax loader in there
var $loader = $('#elgg-widget-loader').clone();
$loader.attr('id', '#elgg-widget-active-loader');
$loader.removeClass('hidden');
$widgetContent.html($loader);
var default_widgets = $("input[name='default_widgets']").val() || 0;
if (default_widgets) {
$(this).append('<input type="hidden" name="default_widgets" value="1">');
}
elgg.action('widgets/save', {
data: $(this).serialize(),
success: function(json) {
$widgetContent.html(json.output);
if (typeof(json.title) != "undefined") {
var $widgetTitle = $widgetContent.parent().parent().find('.elgg-widget-title');
$widgetTitle.html(json.title);
}
}
});
event.preventDefault();
};
/**
* Set the min-height so that all widget column bottoms are the same
*
* This addresses the issue of trying to drag a widget into a column that does
* not have any widgets or many fewer widgets than other columns.
*
* @param {String} selector
* @return void
*/
elgg.ui.widgets.setMinHeight = function(selector) {
var maxBottom = 0;
$(selector).each(function() {
var bottom = parseInt($(this).offset().top + $(this).height());
if (bottom > maxBottom) {
maxBottom = bottom;
}
});
$(selector).each(function() {
var bottom = parseInt($(this).offset().top + $(this).height());
if (bottom < maxBottom) {
var newMinHeight = parseInt($(this).height() + (maxBottom - bottom));
$(this).css('min-height', newMinHeight + 'px');
init: function() {
elgg.ui.widgets._notice();
return widgets.init.apply(this, arguments);
},
add: function () {
elgg.ui.widgets._notice();
return widgets.add.apply(this, arguments);
},
move: function () {
elgg.ui.widgets._notice();
return widgets.move.apply(this, arguments);
},
remove: function () {
elgg.ui.widgets._notice();
return widgets.remove.apply(this, arguments);
},
collapseToggle: function() {
elgg.ui.widgets._notice();
return widgets.collapseToggle.apply(this, arguments);
},
setMinHeight: function() {
elgg.ui.widgets._notice();
return widgets.setMinHeight.apply(this, arguments);
}
});
};
};
require(['jquery'], function ($) {
$(function () {
require(['elgg/widgets'], function (widgets) {
widgets.init();
});
});
elgg.register_hook_handler('init', 'system', widgets.init);
});
@@ -18,6 +18,13 @@
// For backwards compatibility...
echo elgg_view('sprintf.js');
// We use a named AMD module and inline it here instead of using an async call.
// This allows us to bootstrap elgg.ui.widgets library at runtime, without having
// to wait for the module to load. This is necessary to ensure BC for plugins that
// rely on elgg.ui.widgets methods to be available at system init.
// @todo: remove in 3.x and use async calls
echo elgg_view('elgg/widgets.js');
$elggDir = \Elgg\Application::elggDir();
$files = array(
// these must come first
Oops, something went wrong.

0 comments on commit c25c521

Please sign in to comment.