+sub sortable_element {
+ my ($self, $selector, %params) = _hashify(2, @_);
+
+ my %attributes = ( distance => 5,
+ helper => <<'JAVASCRIPT' );
+ function(event, ui) {
+ ui.children().each(function() {
+ $(this).width($(this).width());
+ });
+ return ui;
+ }
+JAVASCRIPT
+
+ my $stop_event = '';
+
+ if ($params{url} && $params{with}) {
+ my $as = $params{as} || $params{with};
+ my $filter = ".filter(function(idx) { return this.substr(0, " . length($params{with}) . ") == '$params{with}'; })";
+ $filter .= ".map(function(idx, str) { return str.replace('$params{with}_', ''); })";
+
+ my $params_js = $params{params} ? qq| + ($params{params})| : '';
+
+ $stop_event = <<JAVASCRIPT;
+ \$.post('$params{url}'${params_js}, { '${as}[]': \$(\$('${selector}').sortable('toArray'))${filter}.toArray() });
+JAVASCRIPT
+ }
+
+ if (!$params{dont_recolor}) {
+ $stop_event .= <<JAVASCRIPT;
+ \$('${selector}>*:odd').removeClass('listrow1').removeClass('listrow0').addClass('listrow0');
+ \$('${selector}>*:even').removeClass('listrow1').removeClass('listrow0').addClass('listrow1');
+JAVASCRIPT
+ }
+
+ if ($stop_event) {
+ $attributes{stop} = <<JAVASCRIPT;
+ function(event, ui) {
+ ${stop_event}
+ return ui;
+ }
+JAVASCRIPT
+ }
+
+ $params{handle} = '.dragdrop' unless exists $params{handle};
+ $attributes{handle} = "'$params{handle}'" if $params{handle};
+
+ my $attr_str = join(', ', map { "${_}: $attributes{$_}" } keys %attributes);
+
+ my $code = <<JAVASCRIPT;
+<script type="text/javascript">
+ \$(function() {
+ \$( "${selector}" ).sortable({ ${attr_str} })
+ });
+</script>
+JAVASCRIPT
+
+ return $code;
+}
+
+sub online_help_tag {
+ my ($self, $tag, %params) = _hashify(2, @_);
+ my $cc = $::myconfig{countrycode};
+ my $file = "doc/online/$cc/$tag.html";
+ my $text = $params{text} || $::locale->text('Help');
+
+ die 'malformed help tag' unless $tag =~ /^[a-zA-Z0-9_]+$/;
+ return unless -f $file;
+ return $self->html_tag('a', $text, href => $file, class => 'jqModal')
+}
+