aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSam Stephenson <sam@37signals.com>2006-02-12 01:30:13 +0000
committerSam Stephenson <sam@37signals.com>2006-02-12 01:30:13 +0000
commit838ec413ebe08be71eea3dec0b061c6f609c839f (patch)
tree8df6658227aeeb613ec809d0da6539bb2d956eeb
parente90bbbdd837ffc83dccbd05528c4f1521e3956ee (diff)
downloadrails-838ec413ebe08be71eea3dec0b061c6f609c839f.tar.gz
rails-838ec413ebe08be71eea3dec0b061c6f609c839f.tar.bz2
rails-838ec413ebe08be71eea3dec0b061c6f609c839f.zip
Add JavaScriptGenerator#replace_element for replacing an element's "outer HTML". Closes #3246.
git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@3579 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
-rw-r--r--actionpack/CHANGELOG2
-rw-r--r--actionpack/lib/action_view/helpers/javascripts/prototype.js47
-rw-r--r--actionpack/lib/action_view/helpers/prototype_helper.rb30
-rw-r--r--actionpack/test/template/prototype_helper_test.rb5
-rw-r--r--railties/html/javascripts/prototype.js47
5 files changed, 83 insertions, 48 deletions
diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG
index 35a8f7bca5..79a77ebe4b 100644
--- a/actionpack/CHANGELOG
+++ b/actionpack/CHANGELOG
@@ -1,5 +1,7 @@
*SVN*
+* Add JavaScriptGenerator#replace_element for replacing an element's "outer HTML". #3246 [tom@craz8.com, Sam Stephenson]
+
* Remove over-engineered form_for code for a leaner implementation. [Nicholas Seckar]
* Document form_for's :html option. [Nicholas Seckar]
diff --git a/actionpack/lib/action_view/helpers/javascripts/prototype.js b/actionpack/lib/action_view/helpers/javascripts/prototype.js
index 62ee54efc1..f37149127f 100644
--- a/actionpack/lib/action_view/helpers/javascripts/prototype.js
+++ b/actionpack/lib/action_view/helpers/javascripts/prototype.js
@@ -436,8 +436,7 @@ var Enumerable = {
var collections = [this].concat(args).map($A);
return this.map(function(value, index) {
- iterator(value = collections.pluck(index));
- return value;
+ return iterator(collections.pluck(index));
});
},
@@ -943,6 +942,19 @@ Object.extend(Element, {
setTimeout(function() {html.evalScripts()}, 10);
},
+ replace: function(element, html) {
+ element = $(element);
+ if (element.outerHTML) {
+ element.outerHTML = html.stripScripts();
+ } else {
+ var range = element.ownerDocument.createRange();
+ range.selectNodeContents(element);
+ element.parentNode.replaceChild(
+ range.createContextualFragment(html.stripScripts()), element);
+ }
+ setTimeout(function() {html.evalScripts()}, 10);
+ },
+
getHeight: function(element) {
element = $(element);
return element.offsetHeight;
@@ -1305,18 +1317,8 @@ var Field = {
$(arguments[i]).value = '';
},
- // Pass the field id or element as the first parameter and optionally a triggering delay in micro-seconds as the second.
- // The delay is useful when the focus is part of effects that won't finish instantly since they prevent the focus from
- // taking hold. Set the delay to right after the effect finishes and the focus will work.
- focus: function() {
- element = $(arguments[0]);
- delay = arguments[1];
-
- if (delay) {
- setTimeout(function() { $(element).focus(); }, delay)
- } else {
- $(element).focus();
- }
+ focus: function(element) {
+ $(element).focus();
},
present: function() {
@@ -1549,16 +1551,15 @@ Form.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
Abstract.EventObserver = function() {}
Abstract.EventObserver.prototype = {
- initialize: function() {
- this.element = $(arguments[0]);
- this.callback = arguments[1];
- this.trigger = arguments[2];
+ initialize: function(element, callback) {
+ this.element = $(element);
+ this.callback = callback;
this.lastValue = this.getValue();
if (this.element.tagName.toLowerCase() == 'form')
this.registerFormCallbacks();
else
- this.registerCallback(this.element, this.trigger);
+ this.registerCallback(this.element);
},
onElementEvent: function() {
@@ -1572,13 +1573,11 @@ Abstract.EventObserver.prototype = {
registerFormCallbacks: function() {
var elements = Form.getElements(this.element);
for (var i = 0; i < elements.length; i++)
- this.registerCallback(elements[i], this.trigger);
+ this.registerCallback(elements[i]);
},
- registerCallback: function(element, trigger) {
- if (trigger && element.type) {
- Event.observe(element, trigger, this.onElementEvent.bind(this));
- } else if (element.type) {
+ registerCallback: function(element) {
+ if (element.type) {
switch (element.type.toLowerCase()) {
case 'checkbox':
case 'radio':
diff --git a/actionpack/lib/action_view/helpers/prototype_helper.rb b/actionpack/lib/action_view/helpers/prototype_helper.rb
index 8aabe04aac..26d877c00a 100644
--- a/actionpack/lib/action_view/helpers/prototype_helper.rb
+++ b/actionpack/lib/action_view/helpers/prototype_helper.rb
@@ -457,6 +457,36 @@ module ActionView
call 'Element.update', id, render(*options_for_render)
end
+ # Replaces the "outer HTML" (i.e., the entire element, not just its
+ # contents) of the DOM element with the given +id+.
+ #
+ # +options_for_render+ may be either a string of HTML to insert, or a hash
+ # of options to be passed to ActionView::Base#render. For example:
+ #
+ # # Replace the DOM element having ID 'person-45' with the
+ # # 'person' partial for the appropriate object.
+ # replace_html 'person-45', :partial => 'person', :object => @person
+ #
+ # This allows the same partial that is used for the +insert_html+ to
+ # be also used for the input to +replace_element+ without resorting to
+ # the use of wrapper elements.
+ #
+ # Examples:
+ #
+ # <div id="people">
+ # <%= render :partial => 'person', :collection => @people %>
+ # </div>
+ #
+ # # Insert a new person
+ # page.insert_html :bottom, :partial => 'person', :object => @person
+ #
+ # # Replace an existing person
+ # page.replace_element 'person_45', :partial => 'person', :object => @person
+ #
+ def replace_element(id, *options_for_render)
+ call 'Element.replace', id, render(*options_for_render)
+ end
+
# Removes the DOM elements with the given +ids+ from the page.
def remove(*ids)
record "#{javascript_object_for(ids)}.each(Element.remove)"
diff --git a/actionpack/test/template/prototype_helper_test.rb b/actionpack/test/template/prototype_helper_test.rb
index eef6841727..fc4ebeccc3 100644
--- a/actionpack/test/template/prototype_helper_test.rb
+++ b/actionpack/test/template/prototype_helper_test.rb
@@ -171,6 +171,11 @@ class JavaScriptGeneratorTest < Test::Unit::TestCase
@generator.replace_html('element', '<p>This is a test</p>')
end
+ def test_replace_element_with_string
+ assert_equal 'Element.replace("element", "<div id=\"element\"><p>This is a test</p></div>");',
+ @generator.replace_element('element', '<div id="element"><p>This is a test</p></div>')
+ end
+
def test_remove
assert_equal '["foo"].each(Element.remove);',
@generator.remove('foo')
diff --git a/railties/html/javascripts/prototype.js b/railties/html/javascripts/prototype.js
index 62ee54efc1..f37149127f 100644
--- a/railties/html/javascripts/prototype.js
+++ b/railties/html/javascripts/prototype.js
@@ -436,8 +436,7 @@ var Enumerable = {
var collections = [this].concat(args).map($A);
return this.map(function(value, index) {
- iterator(value = collections.pluck(index));
- return value;
+ return iterator(collections.pluck(index));
});
},
@@ -943,6 +942,19 @@ Object.extend(Element, {
setTimeout(function() {html.evalScripts()}, 10);
},
+ replace: function(element, html) {
+ element = $(element);
+ if (element.outerHTML) {
+ element.outerHTML = html.stripScripts();
+ } else {
+ var range = element.ownerDocument.createRange();
+ range.selectNodeContents(element);
+ element.parentNode.replaceChild(
+ range.createContextualFragment(html.stripScripts()), element);
+ }
+ setTimeout(function() {html.evalScripts()}, 10);
+ },
+
getHeight: function(element) {
element = $(element);
return element.offsetHeight;
@@ -1305,18 +1317,8 @@ var Field = {
$(arguments[i]).value = '';
},
- // Pass the field id or element as the first parameter and optionally a triggering delay in micro-seconds as the second.
- // The delay is useful when the focus is part of effects that won't finish instantly since they prevent the focus from
- // taking hold. Set the delay to right after the effect finishes and the focus will work.
- focus: function() {
- element = $(arguments[0]);
- delay = arguments[1];
-
- if (delay) {
- setTimeout(function() { $(element).focus(); }, delay)
- } else {
- $(element).focus();
- }
+ focus: function(element) {
+ $(element).focus();
},
present: function() {
@@ -1549,16 +1551,15 @@ Form.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
Abstract.EventObserver = function() {}
Abstract.EventObserver.prototype = {
- initialize: function() {
- this.element = $(arguments[0]);
- this.callback = arguments[1];
- this.trigger = arguments[2];
+ initialize: function(element, callback) {
+ this.element = $(element);
+ this.callback = callback;
this.lastValue = this.getValue();
if (this.element.tagName.toLowerCase() == 'form')
this.registerFormCallbacks();
else
- this.registerCallback(this.element, this.trigger);
+ this.registerCallback(this.element);
},
onElementEvent: function() {
@@ -1572,13 +1573,11 @@ Abstract.EventObserver.prototype = {
registerFormCallbacks: function() {
var elements = Form.getElements(this.element);
for (var i = 0; i < elements.length; i++)
- this.registerCallback(elements[i], this.trigger);
+ this.registerCallback(elements[i]);
},
- registerCallback: function(element, trigger) {
- if (trigger && element.type) {
- Event.observe(element, trigger, this.onElementEvent.bind(this));
- } else if (element.type) {
+ registerCallback: function(element) {
+ if (element.type) {
switch (element.type.toLowerCase()) {
case 'checkbox':
case 'radio':