+ns.find_text_block_picture_id = function(clicked_elt) {
+  var id    = $(clicked_elt).attr('id');
+  var match = id.match(/^text-block-picture-(\d+)$/);
+  if (match)
+    return match[1] * 1;
+
+  return undefined;
+};
+
+ns.add_edit_text_block_picture_ajax_call = function(key, opt) {
+  var title = key == 'add_picture' ? kivi.t8('Add picture to text block') : kivi.t8('Edit picture');
+
+  kivi.popup_dialog({ url:    'controller.pl',
+                      data:   { action:     'RequirementSpecTextBlock/ajax_' + key,
+                                id:         ns.find_text_block_id(opt.$trigger),
+                                picture_id: ns.find_text_block_picture_id(opt.$trigger) },
+                      dialog: { title:      title }});
+
+  return true;
+};
+
+ns.standard_text_block_picture_ajax_call = function(key, opt) {
+  var data = {
+      action: "RequirementSpecTextBlock/ajax_" + key
+    , id:     ns.find_text_block_picture_id(opt.$trigger)
+  };
+
+  if (key == 'download_picture')
+    $.download("controller.pl", data);
+  else
+    $.post("controller.pl", data, kivi.eval_json_result);
+
+  return true;
+};
+
+ns.ask_delete_text_block_picture = function(key, opt) {
+  if (confirm(kivi.t8("Are you sure?")))
+    ns.standard_text_block_picture_ajax_call(key, opt);
+  return true;
+};
+
+ns.handle_text_block_picture_popup_menu_markings = function(opt, add) {
+  var id = ns.find_text_block_picture_id(opt.$trigger);
+  if (id)
+    $('#text-block-picture-' + id ).toggleClass('selected', add);
+  return true;
+};
+
+ns.text_block_picture_popup_menu_shown = function(opt) {
+  return ns.handle_text_block_picture_popup_menu_markings(opt, true);
+};
+
+ns.text_block_picture_popup_menu_hidden = function(opt) {
+  return ns.handle_text_block_picture_popup_menu_markings(opt, false);
+};
+
+ns.make_text_block_picture_lists_sortable = function() {
+  kivi.run_once_for(".requirement-spec-text-block-picture-list", 'make-text-block-picture-list-sortable', function($elt) {
+    $elt.sortable({
+      stop: function(event, ui) {
+        $.post('controller.pl?action=RequirementSpecTextBlock/reorder_pictures', {
+          'picture_id[]': $($elt.sortable('toArray')).map(function(idx, str) { return str.replace('text-block-picture-', ''); }).toArray()
+        });
+        return ui;
+      }
+      , distance: 5
+    });
+  });
+};
+