views:

941

answers:

3

I love Prototype but it doesn't come with any widgets. In particular, it provides nothing for window/dialog creation.

I've tried the Prototype Window add-on library but found it unreliable and it hasn't been maintained for a long time.

Anyone have any recommended solutions? Or any recipes/patterns for creating a dialog from first principles (i.e. with no library other than Prototype)? I need modal behavior (with faded background effect) and I have to support IE6 as well as modern browsers.

Just to be clear, I'm not looking to switch away from Prototype and I don't really want to include an additional base library such as JQuery.

A: 

Scriptaculous is a UI/Widget/effects library built on top of Prototype. You're bound to find what you're looking for there.

meouw
Actually, no - Scriptaculous is mostly about effects.
David Easley
+1  A: 

There are a few good sites to look at:

Prototype UI - which has a modal dialog.

Scripteka - The mother lode of Prototype add-ons. Lots of great stuff here.

Diodeus
David Easley
Ah, after trying the Dialog code from Scripteka, it didn't work in my CSS layout (dialog offset way to the left), so no go. See my own answer below.
David Easley
+1  A: 

For a really simple dialog, checkout this code: http://snippets.dzone.com/posts/show/3411

Very minimal and simple - just what I wanted.

The code went through a few modifications. Here's the final version that worked for me:

/* 
 * Adapted from http://snippets.dzone.com/posts/show/3411
 */

var Dialog = {};
Dialog.Box = Class.create();
Object.extend(Dialog.Box.prototype, {
  initialize: function(id) {
    this.createOverlay();

    this.dialog_box = $(id);
    this.dialog_box.show = this.show.bind(this);
    this.dialog_box.persistent_show = this.persistent_show.bind(this);
    this.dialog_box.hide = this.hide.bind(this);

    this.parent_element = this.dialog_box.parentNode;

    this.dialog_box.style.position = "absolute";

    var e_dims = Element.getDimensions(this.dialog_box);
    var b_dims = Element.getDimensions(this.overlay);

    this.dialog_box.style.left = ((b_dims.width/2) - (e_dims.width/2)) + 'px';
    this.dialog_box.style.top = this.getScrollTop() + ((this.winHeight() - (e_dims.width/2))/2) + 'px';
    this.dialog_box.style.zIndex = this.overlay.style.zIndex + 1;
  },

  createOverlay: function() {
    if ($('dialog_overlay')) {
      this.overlay = $('dialog_overlay');
    } else {
      this.overlay = document.createElement('div');
      this.overlay.id = 'dialog_overlay';
      Object.extend(this.overlay.style, {
        position: 'absolute',
        top: 0,
        left: 0,
        zIndex: 90,
        width: '100%',
        backgroundColor: '#000',
        display: 'none'
      });
      document.body.insertBefore(this.overlay, document.body.childNodes[0]);
    }
  },

  moveDialogBox: function(where) {
    Element.remove(this.dialog_box);
    if (where == 'back')
      this.dialog_box = this.parent_element.appendChild(this.dialog_box);
    else
      this.dialog_box = this.overlay.parentNode.insertBefore(this.dialog_box, this.overlay);
  },

  show: function(optHeight/* optionally override the derived height, which often seems to be short. */) {
    this.overlay.style.height = this.winHeight()+'px';
    this.moveDialogBox('out');

    this.overlay.onclick = this.hide.bind(this);

    this.selectBoxes('hide');
    new Effect.Appear(this.overlay, {duration: 0.1, from: 0.0, to: 0.3});
    this.dialog_box.style.display = '';

    this.dialog_box.style.left = '0px';

    var e_dims = Element.getDimensions(this.dialog_box);

    this.dialog_box.style.left = (this.winWidth() - e_dims.width)/2 + 'px';

    var h = optHeight || (e_dims.height + 200);
    this.dialog_box.style.top = this.getScrollTop() + (this.winHeight() - h/2)/2 + 'px';
  },

  getScrollTop: function() {
    return (window.pageYOffset)?window.pageYOffset:(document.documentElement && document.documentElement.scrollTop)?document.documentElement.scrollTop:document.body.scrollTop;
  },

  persistent_show: function() {
    this.overlay.style.height = this.winHeight()+'px';
    this.moveDialogBox('out');

    this.selectBoxes('hide');
    new Effect.Appear(this.overlay, {duration: 0.1, from: 0.0, to: 0.3});

    this.dialog_box.style.display = '';
    this.dialog_box.style.left = '0px';
    var e_dims = Element.getDimensions(this.dialog_box);
    this.dialog_box.style.left = (this.winWidth()/2 - e_dims.width/2) + 'px';
  },

  hide: function() {
    this.selectBoxes('show');
    new Effect.Fade(this.overlay, {duration: 0.1});
    this.dialog_box.style.display = 'none';
    this.moveDialogBox('back');
    $A(this.dialog_box.getElementsByTagName('input')).each( function(e) {
      if (e.type != 'submit' && e.type != 'button') e.value = '';
    });
  },

  selectBoxes: function(what) {
    $A(document.getElementsByTagName('select')).each(function(select) {
      Element[what](select);
    });

    if (what == 'hide')
      $A(this.dialog_box.getElementsByTagName('select')).each(function(select){Element.show(select)})
  },

  winWidth: function() {
    if (typeof window.innerWidth != 'undefined')
       return window.innerWidth;
    if (typeof document.documentElement != 'undefined' && typeof document.documentElement.clientWidth != 'undefined' && document.documentElement.clientWidth != 0)
       return document.documentElement.clientWidth;
    return document.getElementsByTagName('body')[0].clientWidth
  },
  winHeight: function() {
    if (typeof window.innerHeight != 'undefined')
      return window.innerHeight
    if (typeof document.documentElement != 'undefined' && typeof document.documentElement.clientHeight != 'undefined' && document.documentElement.clientHeight != 0)
      return document.documentElement.clientHeight;
    return document.getElementsByTagName('body')[0].clientHeight;
  }

});
David Easley