- Patch #107061 by Steven et al: add jQuery teaser splitter.

6.x
Dries Buytaert 2007-04-09 13:58:03 +00:00
parent 15d739a504
commit dcbb5fa3f4
11 changed files with 226 additions and 23 deletions

View File

@ -8,6 +8,7 @@ Drupal 6.0, xxxx-xx-xx (development version)
- Drupal works with error reporting set to E_ALL.
- Added scripts/drupal.sh to execute Drupal code from the command line. Useful to use Drupal as a framework to build command-line tools.
- Used the Garland theme for the installation and maintenance pages.
- Improved handling of teasers in posts.
- Added generic language management functionality.
* Support for right to left scripts.
* Language detection based on parts of the URL.

View File

@ -1452,6 +1452,18 @@ function theme_form($element) {
*/
function theme_textarea($element) {
$class = array('form-textarea');
// Add teaser behaviour (must come before resizable)
if (!empty($element['#teaser'])) {
drupal_add_js('misc/teaser.js');
// Note: arrays are merged in drupal_get_js().
drupal_add_js(array('teaserButton' => array(t('Join summary'), t('Split summary at cursor'))), 'setting');
drupal_add_js(array('teaserCheckbox' => array($element['#id'] => $element['#teaser_checkbox'])), 'setting');
drupal_add_js(array('teaser' => array($element['#id'] => $element['#teaser'])), 'setting');
$class[] = 'teaser';
}
// Add resizable behaviour
if ($element['#resizable'] !== FALSE) {
drupal_add_js('misc/textarea.js');
$class[] = 'resizable';

View File

@ -200,6 +200,26 @@ Drupal.encodeURIComponent = function (item, uri) {
return uri.indexOf('?q=') ? item : item.replace('%26', '%2526').replace('%23', '%2523');
};
/**
* Get the text selection in a textarea.
*/
Drupal.getSelection = function (element) {
if (typeof(element.selectionStart) != 'number' && document.selection) {
// The current selection
var range1 = document.selection.createRange();
var range2 = range1.duplicate();
// Select all text.
range2.moveToElementText(element);
// Now move 'dummy' end point to end point of original range.
range2.setEndPoint('EndToEnd', range1);
// Now we can calculate start and end points.
var start = range2.text.length - range1.text.length;
var end = start + range1.text.length;
return { 'start': start, 'end': end };
}
return { 'start': element.selectionStart, 'end': element.selectionEnd };
}
// Global Killswitch on the <html> element
if (Drupal.jsEnabled) {
document.documentElement.className = 'js';

80
misc/teaser.js Normal file
View File

@ -0,0 +1,80 @@
// $Id$
/**
* Auto-attach for teaser behaviour.
*
* Note: depends on resizable textareas.
*/
Drupal.teaserAttach = function() {
$('textarea.teaser:not(.joined)').each(function() {
var teaser = $(this).addClass('joined');
// Move teaser textarea before body, and remove its form-item wrapper.
var body = $('#'+ Drupal.settings.teaser[this.id]);
var checkbox = $('#'+ Drupal.settings.teaserCheckbox[this.id]).parent();
var parent = teaser[0].parentNode;
$(body).before(teaser);
$(parent).remove();
function trim(text) {
return text.replace(/^\s+/g, '').replace(/\s+$/g, '');
}
// Join the teaser back to the body.
function join_teaser() {
if (teaser.val()) {
body.val(trim(teaser.val()) +'\r\n\r\n'+ trim(body.val()));
}
// Hide and disable teaser
$(teaser).attr('disabled', 'disabled');
$(teaser).parent().slideUp('fast');
// Change label
$(this).val(Drupal.settings.teaserButton[1]);
// Show separate teaser checkbox
$(checkbox).hide();
}
// Split the teaser from the body.
function split_teaser() {
body[0].focus();
var selection = Drupal.getSelection(body[0]);
var split = selection.start;
var text = body.val();
// Note: using val() fails sometimes. jQuery bug?
teaser[0].value = trim(text.slice(0, split));
body[0].value = trim(text.slice(split));
// Reveal and enable teaser
$(teaser).attr('disabled', '');
$(teaser).parent().slideDown('fast');
// Change label
$(this).val(Drupal.settings.teaserButton[0]);
// Show separate teaser checkbox
$(checkbox).show();
}
// Add split/join button.
var button = $('<div class="teaser-button-wrapper"><input type="button" class="teaser-button" /></div>');
var include = $('#'+ this.id.substring(0, this.id.length - 2) +'include');
$(include).parent().parent().before(button);
// Extract the teaser from the body, if set. Otherwise, stay in joined mode.
var text = body.val().split('<!--break-->', 2);
if (text.length == 2) {
teaser[0].value = trim(text[0]);
body[0].value = trim(text[1]);
$(teaser).attr('disabled', '');
$('input', button).val(Drupal.settings.teaserButton[0]).toggle(join_teaser, split_teaser);
}
else {
$(teaser).hide();
$('input', button).val(Drupal.settings.teaserButton[1]).toggle(split_teaser, join_teaser);
$(checkbox).hide();
}
});
}
if (Drupal.jsEnabled) {
$(document).ready(Drupal.teaserAttach);
}

View File

@ -7,6 +7,12 @@ Drupal.textareaAttach = function() {
$(this).wrap('<div class="resizable-textarea"></div>')
.parent().append($('<div class="grippie"></div>').mousedown(startDrag));
// Inherit visibility
if ($(this).is(':hidden')) {
$(this).parent().hide();
$(this).show();
}
var grippie = $('div.grippie', $(this).parent())[0];
grippie.style.marginRight = (grippie.offsetWidth - $(this)[0].offsetWidth) +'px';

View File

@ -595,8 +595,8 @@ function block_box_form($edit = array()) {
'#required' => TRUE,
'#weight' => -19,
);
$form['body_filter']['#weight'] = -17;
$form['body_filter']['body'] = array(
$form['body_field']['#weight'] = -17;
$form['body_field']['body'] = array(
'#type' => 'textarea',
'#title' => t('Block body'),
'#default_value' => $edit['body'],
@ -607,7 +607,7 @@ function block_box_form($edit = array()) {
if (!isset($edit['format'])) {
$edit['format'] = FILTER_FORMAT_DEFAULT;
}
$form['body_filter']['format'] = filter_form($edit['format'], -16);
$form['body_field']['format'] = filter_form($edit['format'], -16);
return $form;
}

View File

@ -211,8 +211,7 @@ function blog_form(&$node) {
}
$form['title'] = array('#type' => 'textfield', '#title' => check_plain($type->title_label), '#required' => TRUE, '#default_value' => !empty($node->title) ? $node->title : NULL, '#weight' => -5);
$form['body_filter']['body'] = array('#type' => 'textarea', '#title' => check_plain($type->body_label), '#default_value' => !empty($node->body) ? $node->title : NULL, '#rows' => 20, '#required' => TRUE);
$form['body_filter']['filter'] = filter_form($node->format);
$form['body_field'] = node_body_field($node, $type->body_label, $type->min_word_count);
return $form;
}

View File

@ -232,13 +232,8 @@ function book_form(&$node) {
'#default_value' => $node->title,
'#weight' => -5,
);
$form['body_filter']['body'] = array('#type' => 'textarea',
'#title' => check_plain($type->body_label),
'#default_value' => $node->body,
'#rows' => 20,
'#required' => TRUE,
);
$form['body_filter']['format'] = filter_form($node->format);
$form['body_field'] = node_body_field($node, $type->body_label, 1);
if (user_access('administer nodes')) {
$form['weight'] = array('#type' => 'weight',

View File

@ -423,8 +423,7 @@ function forum_form(&$node) {
$form['shadow'] = array('#type' => 'checkbox', '#title' => t('Leave shadow copy'), '#default_value' => $shadow, '#description' => t('If you move this topic, you can leave a link in the old forum to the new forum.'));
}
$form['body_filter']['body'] = array('#type' => 'textarea', '#title' => check_plain($type->body_label), '#default_value' => !empty($node->body) ? $node->body : '', '#rows' => 20, '#required' => TRUE);
$form['body_filter']['format'] = filter_form($node->format);
$form['body_field'] = node_body_field($node, $type->body_label, 1);
return $form;
}

View File

@ -170,6 +170,28 @@ function node_mark($nid, $timestamp) {
return MARK_READ;
}
/**
* See if the user used JS to submit a teaser.
*/
function node_teaser_js(&$form, $form_values) {
// Glue the teaser to the body.
if (isset($form['#post']['teaser_js'])) {
if (trim($form_values['teaser_js'])) {
// Space the teaser from the body
$body = trim($form_values['teaser_js']) ."\r\n<!--break-->\r\n". trim($form_values['body']);
}
else {
// Empty teaser, no spaces.
$body = '<!--break-->'. $form_values['body'];
}
// Pass value onto preview/submit
form_set_value($form['body'], $body);
// Pass value back onto form
$form['body']['#value'] = $body;
}
return $form;
}
/**
* Automatically generate a teaser for a node body in a given format.
*/
@ -1876,7 +1898,16 @@ function node_submit($node) {
// Auto-generate the teaser, but only if it hasn't been set (e.g. by a
// module-provided 'teaser' form item).
if (!isset($node->teaser)) {
$node->teaser = isset($node->body) ? node_teaser($node->body, isset($node->format) ? $node->format : NULL) : '';
if (isset($node->body)) {
$node->teaser = node_teaser($node->body, isset($node->format) ? $node->format : NULL);
// Chop off the teaser from the body if needed.
if (!$node->teaser_include && $node->teaser == substr($node->body, 0, strlen($node->teaser))) {
$node->body = substr($node->body, strlen($node->teaser));
}
}
else {
$node->teaser = '';
}
}
if (user_access('administer nodes')) {
@ -2216,6 +2247,10 @@ function node_preview($node) {
// 'teaser' form item).
if (!isset($node->teaser)) {
$node->teaser = empty($node->body) ? '' : node_teaser($node->body, $node->format);
// Chop off the teaser from the body if needed.
if (!$node->teaser_include && $node->teaser == substr($node->body, 0, strlen($node->teaser))) {
$node->body = substr($node->body, strlen($node->teaser));
}
}
// Display a preview of the node:
@ -2241,7 +2276,7 @@ function node_preview($node) {
function theme_node_preview($node) {
$output = '<div class="preview">';
if ($node->teaser && $node->teaser != $node->body) {
drupal_set_message(t('The trimmed version of your post shows what your post looks like when promoted to the main page or when exported for syndication. You can insert the delimiter "&lt;!--break--&gt;" (without the quotes) to fine-tune where your post gets split.'));
drupal_set_message(t('The trimmed version of your post shows what your post looks like when promoted to the main page or when exported for syndication.<span class="no-js"> You can insert the delimiter "&lt;!--break--&gt;" (without the quotes) to fine-tune where your post gets split.</span>'));
$output .= '<h3>'. t('Preview trimmed version') .'</h3>';
$output .= node_view(drupal_clone($node), 1, FALSE, 0);
$output .= '<h3>'. t('Preview full version') .'</h3>';
@ -2957,6 +2992,43 @@ function node_content_access($op, $node) {
}
}
/**
* Return a node body field, with format and teaser.
*/
function node_body_field(&$node, $label, $word_count) {
// Check if we need to restore the teaser at the beginning of the body.
$include = !isset($node->teaser) || ($node->teaser == substr($node->body, 0, strlen($node->teaser)));
$form = array(
'#after_build' => array('node_teaser_js'));
$form['teaser_js'] = array(
'#type' => 'textarea',
'#rows' => 10,
'#teaser' => 'edit-body',
'#teaser_checkbox' => 'edit-teaser-include',
'#disabled' => TRUE);
$form['teaser_include'] = array(
'#type' => 'checkbox',
'#title' => t('Show summary in full view'),
'#default_value' => $include,
'#prefix' => '<div class="teaser-checkbox">',
'#suffix' => '</div>',
);
$form['body'] = array(
'#type' => 'textarea',
'#title' => check_plain($label),
'#default_value' => $include ? $node->body : ($node->teaser . $node->body),
'#rows' => 20,
'#required' => ($word_count > 0));
$form['format'] = filter_form($node->format);
return $form;
}
/**
* Implementation of hook_form().
*/
@ -2975,13 +3047,7 @@ function node_content_form($node) {
}
if ($type->has_body) {
$form['body_filter']['body'] = array(
'#type' => 'textarea',
'#title' => check_plain($type->body_label),
'#default_value' => $node->body,
'#rows' => 20,
'#required' => ($type->min_word_count > 0));
$form['body_filter']['format'] = filter_form($node->format);
$form['body_field'] = node_body_field($node, $type->body_label, $type->min_word_count);
}
return $form;

View File

@ -357,6 +357,31 @@ html.js .resizable-textarea textarea {
display: block;
}
/*
** Teaser splitter
*/
.joined + .grippie {
height: 5px;
background-position: center 1px;
margin-bottom: -2px;
}
div.teaser-button-wrapper {
float: right;
padding-right: 5%;
margin: 0;
}
.teaser-checkbox div.form-item {
float: right;
margin: 0 5% 0 0;
padding: 0;
}
textarea.teaser {
display: none;
}
html.js .no-js {
display: none;
}
/*
** Progressbar styles
*/