From 5e49dd2a68aaa18db498b6b139e76c19f5c32fda Mon Sep 17 00:00:00 2001
From: Steven Wittens <steven@10.no-reply.drupal.org>
Date: Thu, 29 Dec 2005 03:59:30 +0000
Subject: [PATCH] - #42446: Resizable textareas.

---
 includes/form.inc |   8 +++-
 misc/drupal.css   |  14 +++++++
 misc/drupal.js    |  18 ++++++++
 misc/textarea.js  | 105 ++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 144 insertions(+), 1 deletion(-)
 create mode 100644 misc/textarea.js

diff --git a/includes/form.inc b/includes/form.inc
index f6043cb1bbbe..4121d961d149 100644
--- a/includes/form.inc
+++ b/includes/form.inc
@@ -760,9 +760,15 @@ function theme_form($element) {
  *   A themed HTML string representing the textarea.
  */
 function theme_textarea($element) {
+  $class = 'textarea';
+  if ($element['#resizable'] !== false) {
+    drupal_add_js('misc/textarea.js');
+    $class .= ' resizable';
+  }
+
   $cols = $element['#cols'] ? ' cols="'. $element['#cols'] .'"' : '';
 
-  return theme('form_element', $element['#title'], '<textarea'. $cols .' rows="'. $element['#rows'] .'" name="'. $element['#name'] .'" id="' . $element['#id'] .'" class="'. _form_get_class('textarea', $element['#required'], form_get_error($element)) .'"'. drupal_attributes($element['#attributes']) .'>'. check_plain($element['#value']) .'</textarea>', $element['#description'], $element['#id'], $element['#required'], form_get_error($element));
+  return theme('form_element', $element['#title'], '<textarea'. $cols .' rows="'. $element['#rows'] .'" name="'. $element['#name'] .'" id="' . $element['#id'] .'" class="'. _form_get_class($class, $element['#required'], form_get_error($element)) .'"'. drupal_attributes($element['#attributes']) .'>'. check_plain($element['#value']) .'</textarea>', $element['#description'], $element['#id'], $element['#required'], form_get_error($element));
 }
 
 /**
diff --git a/misc/drupal.css b/misc/drupal.css
index 18c37bfd16da..63813ddab35c 100644
--- a/misc/drupal.css
+++ b/misc/drupal.css
@@ -618,3 +618,17 @@ html.js fieldset.collapsed legend a {
 * html.js fieldset.collapsible legend a {
   display: block;
 }
+
+/*
+** Resizable text areas
+*/
+.resizable-textarea {
+  width: 95%;
+}
+.resizable-textarea .grippie {
+  height: 14px;
+  background: #eee url('grippie.png') no-repeat 100% 100%;
+  border: 1px solid #ddd;
+  border-top-width: 0px;
+  cursor: ns-resize;
+}
diff --git a/misc/drupal.js b/misc/drupal.js
index e64d3bc6010c..c19dc565a45d 100644
--- a/misc/drupal.js
+++ b/misc/drupal.js
@@ -211,6 +211,10 @@ function absolutePosition(el) {
   return r;
 };
 
+function dimensions(el) {
+  return { width: el.offsetWidth, height: el.offsetHeight };
+}
+
 /**
  * Returns true if an element has a specified class name
  */
@@ -279,6 +283,20 @@ function removeNode(node) {
   }
 }
 
+/**
+ * Prevents an event from propagating.
+ */
+function stopEvent(event) {
+  if (event.preventDefault) {
+    event.preventDefault();
+    event.stopPropagation();
+  }
+  else {
+    event.returnValue = false;
+    event.cancelBubble = true;
+  }  
+}
+
 /**
  * Wrapper around document.getElementById().
  */
diff --git a/misc/textarea.js b/misc/textarea.js
new file mode 100644
index 000000000000..32efb330539e
--- /dev/null
+++ b/misc/textarea.js
@@ -0,0 +1,105 @@
+if (isJsEnabled()) {
+  addLoadEvent(function() {
+    // Attach to all textareas
+    textareas = document.getElementsByTagName('textarea');
+    var textarea;
+    for (var i = 0; textarea = textareas[i]; ++i) {
+      if (hasClass(textarea, 'resizable')) {
+        new textArea(textarea);        
+      }
+    }
+  });
+}
+
+function textArea(element) {
+  var ta = this;
+  this.element = element;
+  this.parent = this.element.parentNode;
+  this.dimensions = dimensions(element);
+  
+  // Prepare wrapper
+  this.wrapper = document.createElement('div');
+  this.wrapper.className = 'resizable-textarea';
+  this.parent.insertBefore(this.wrapper, this.element);
+
+  // Add grippie and measure it
+  this.grippie = document.createElement('div');
+  this.grippie.className = 'grippie';
+  this.wrapper.appendChild(this.grippie);
+  this.grippie.dimensions = dimensions(this.grippie);
+  this.grippie.onmousedown = function (e) { ta.beginDrag(e); };
+  this.element.onmouseup = function (e) { ta.endDrag(e); };
+
+  // Set wrapper and textarea dimensions
+  this.wrapper.style.height = this.dimensions.height + this.grippie.dimensions.height + 1 +'px';
+  this.element.style.marginBottom = '0px';
+  this.element.style.width = '100%';
+  this.element.style.height = this.dimensions.height +'px';
+
+  // Wrap textarea
+  removeNode(this.element);
+  this.wrapper.insertBefore(this.element, this.grippie);
+
+  // Measure difference between desired and actual textarea dimensions to account for padding/borders
+  this.widthOffset = dimensions(this.wrapper).width - this.dimensions.width;
+
+  // Make the grippie line up in various browsers
+  if (window.opera) {
+    // Opera
+    this.grippie.style.marginRight = this.widthOffset +'px';
+  }
+  if (document.all && !window.opera) {
+    // IE
+    this.grippie.style.width = '100%';
+    this.grippie.style.paddingLeft = '2px';
+  }
+  // Mozilla
+  this.element.style.MozBoxSizing = 'border-box';  
+
+  this.heightOffset = absolutePosition(this.grippie).y - absolutePosition(this.element).y - this.dimensions.height;
+}
+
+textArea.prototype.beginDrag = function (event) {
+  event = event || window.event;
+  // Capture mouse
+  var cp = this;
+  this.oldMoveHandler = document.onmousemove;
+  document.onmousemove = function(e) { cp.handleDrag(e); };
+  this.oldUpHandler = document.onmouseup;
+  document.onmouseup = function(e) { cp.endDrag(e); };
+
+  // Store drag offset from grippie top
+  var pos = absolutePosition(this.grippie);
+  this.dragOffset = event.clientY - pos.y;
+
+  // Make transparent
+  this.element.style.opacity = 0.5;
+
+  // Process
+  this.handleDrag(event);
+}
+
+textArea.prototype.handleDrag = function (event) {
+  event = event || window.event;
+  // Get coordinates relative to text area
+  var pos = absolutePosition(this.element);
+  var y = event.clientY - pos.y;
+
+  // Set new height
+  var height = Math.max(32, y - this.dragOffset - this.heightOffset);
+  this.wrapper.style.height = height + this.grippie.dimensions.height + 1 + 'px';
+  this.element.style.height = height + 'px';
+
+  // Avoid text selection
+  stopEvent(event);
+}
+
+textArea.prototype.endDrag = function (event) {
+  // Uncapture mouse
+  document.onmousemove = this.oldMoveHandler;
+  document.onmouseup = this.oldUpHandler;
+
+  // Restore opacity
+  this.element.style.opacity = 1.0;
+}
+
-- 
GitLab