On 7/19/06, Todd Ross <[EMAIL PROTECTED]> wrote:
Peter Michaux wrote:
> On 7/19/06, Todd Ross <[EMAIL PROTECTED]> wrote:
>
>> but I still believe in Prototype-the-project.
>
> I'm curious why.

I've already eluded to the why.  Its the cleanest, most elegant
JavaScript code base that I've ever laid eyes on.

It may be the cleanest and most elegant that you've seen but it is not
clean or elegant. For example, look at the insertion code

Here is the code that for Insert.Before. Not shown are 10 lines for
the dollar sign function, 7 lines for Class, and a bunch more for the
unnecessary enumerable methods.

You have to really dig in and do a bit of analysis to see how
convoluting things are here. Then compare with the rewritten example
at the bottom.

Abstract.Insertion = function(adjacency) {
 this.adjacency = adjacency;
}

Abstract.Insertion.prototype = {
 initialize: function(element, content) {
   this.element = $(element);
   this.content = content.stripScripts();

   if (this.adjacency && this.element.insertAdjacentHTML) {
     try {
       this.element.insertAdjacentHTML(this.adjacency, this.content);
     } catch (e) {
       var tagName = this.element.tagName.toLowerCase();
       if (tagName  'tbody' || tagName  'tr') {
         this.insertContent(this.contentFromAnonymousTable());
       } else {
         throw e;
       }
     }
   } else {
     this.range = this.element.ownerDocument.createRange();
     if (this.initializeRange) this.initializeRange();
     this.insertContent([this.range.createContextualFragment(this.content)]);
   }

   setTimeout(function() {content.evalScripts()}, 10);
 },

 contentFromAnonymousTable: function() {
   var div = document.createElement('div');
   div.innerHTML = ''  this.content  '
';
   return $A(div.childNodes0.childNodes0.childNodes);
 }
}

var Insertion = new Object();

Insertion.Before = Class.create(); Insertion.Before.prototype =
Object.extend(new Abstract.Insertion('beforeBegin'), {
initializeRange: function() { this.range.setStartBefore(this.element);
}, });

insertContent: function(fragments) {
 fragments.each((function(fragment) {
   this.element.parentNode.insertBefore(fragment, this.element);
 }).bind(this));
}

The above code is strange enough with Class.create() and Object.extend
but what gets really strange is when you want to use all of this and
actually make an insertion before. To make a single insertion we have
to construct a new object that then makes the insertion during
initialization. This is not efficient.

new Insertion.Before(element, content);

A better way

The Prototype.js approach using the initializeRange() and
insertContent() is not far off the normal way of doing things in
JavaScript. This is the idea of having a prototypical object and
instances of this object with some properties that vary.

Below is the insertion code refactored a more usual JavaScript way.
Notice there is no need for support code like Prototype.js'
Class.create(), Object.extend, $() or any enumerable methods. This is
much more efficient, modular, maintainable and robust.

Note that this code is tested but not completely.

var Fork = {};

Fork.Inserter = function(adjacency) {
 this.adjacency = adjacency;
};

Fork.Inserter.prototype.insert = function(element, content) {
 this.element = (typeof element === "string") ?
                document.getElementById(element) : element;
 this.content = Fork.stripScripts(content);

 if (this.element.insertAdjacentHTML) {
   try {
     this.element.insertAdjacentHTML(this.adjacency, this.content);
   } catch (e) {
     var tagName = this.element.tagName.toLowerCase();
     if (tagName === 'tbody' || tagName === 'tr') {
       this.insertContent(this.contentFromAnonymousTable());
     } else {
       throw e;
     }
   }
 } else {
   this.range = this.element.ownerDocument.createRange();
   this.initRange();
   this.insertContent([this.range.createContextualFragment(this.content)]);
 }

 Fork.insertScripts(this.element);
};

Fork.Inserter.prototype.contentFromAnonymousTable = function() {
 var div = document.createElement('div');
 div.innerHTML = '<table><tbody>' + this.content + '</tbody></table>';
 return div.childNodes[0].childNodes[0].childNodes;
};

Fork.insertBefore = new Fork.Inserter('beforeBegin');

Fork.insertBefore.initRange = function() {
 this.range.setStartBefore(this.element);
};

Fork.insertBefore.insertContent = function(fragments) {
 for(var i=0;i<fragments.length;i++){
   this.element.parentNode.insertBefore(fragments[i], this.element);
 }
};

The best part comes when actually making an insertion.

Fork.insertBefore.insert(element, content);

No confusing need to instantiate a new object for every insertion.
_______________________________________________
Rails-spinoffs mailing list
Rails-spinoffs@lists.rubyonrails.org
http://lists.rubyonrails.org/mailman/listinfo/rails-spinoffs

Reply via email to