PageSourceSearch

https://store.sandiegomarine.com/inet/util/jquery.boxy.js

js sandiegomarine.com collected 2026-10-09 05:11:53 UTC 20,719 bytes, 571 lines download raw bytes

1/**
2 * Boxy 0.1.4 - Facebook-style dialog, with frills
3 *
4 * (c) 2008 Jason Frame
5 * Licensed under the MIT License (LICENSE)
6 */
7 
8/*
9 * jQuery plugin
10 *
11 * Options:
12 *   message: confirmation message for form submit hook (default: "Please confirm:")
13 * 
14 * Any other options - e.g. 'clone' - will be passed onto the boxy constructor (or
15 * Boxy.load for AJAX operations)
16 */
17jQuery.fn.boxy = function(options) {
18    options = options || {};
19    return this.each(function() {      
20        var node = this.nodeName.toLowerCase(), self = this;
21        if (node == 'a') {
22            jQuery(this).on('click', function() {
23                var active = Boxy.linkedTo(this),
24                    href = this.getAttribute('href'),
25                    localOptions = jQuery.extend({actuator: this, title: this.title}, options);
26                    
27                if (active) {
28                    active.show();
29                } else if (href.indexOf('#') >= 0) {
30                    var content = jQuery(href.substr(href.indexOf('#'))),
31                        newContent = content.clone(true);
32                    content.remove();
33                    localOptions.unloadOnHide = false;
34                    new Boxy(newContent, localOptions);
35                } else { // fall back to AJAX; could do with a same-origin check
36                    if (!localOptions.cache) localOptions.unloadOnHide = true;
37                    Boxy.load(this.href, localOptions);
38                }
39                
40                return false;
41            });
42        } else if (node == 'form') {
43            jQuery(this).on('submit.boxy', function() {
44                Boxy.confirm(options.message || 'Please confirm:', function() {
45                    jQuery(self).off('submit.boxy').submit();
46                });
47                return false;
48            });
49        }
50    });
51};
52
53//
54// Boxy Class
55
56function Boxy(element, options) {
57    
58    this.boxy = jQuery(Boxy.WRAPPER);
59    jQuery.data(this.boxy[0], 'boxy', this);
60    
61    this.visible = false;
62    this.options = jQuery.extend({}, Boxy.DEFAULTS, options || {});
63    
64    if (this.options.modal) {
65        this.options = jQuery.extend(this.options, {center: true, draggable: false});
66    }
67    
68    // options.actuator == DOM element that opened this boxy
69    // association will be automatically deleted when this boxy is remove()d
70    if (this.options.actuator) {
71        jQuery.data(this.options.actuator, 'active.boxy', this);
72    }
73    
74    this.setContent(element || "<div></div>");
75    this._setupTitleBar();
76    
77    this.boxy.css('display', 'none').appendTo(document.body);
78    this.toTop();
79
80    if (this.options.fixed) {
81        // if (jQuery.browser.msie && jQuery.browser.version < 7) {
82        //     this.options.fixed = false; // IE6 doesn't support fixed positioning
83        // } else {
84        //     this.boxy.addClass('fixed');
85        // }
86        this.boxy.addClass('fixed');
87    }
88    
89    if (this.options.center && Boxy._u(this.options.x, this.options.y)) {
90        this.center();
91    } else {
92        this.moveTo(
93            Boxy._u(this.options.x) ? this.options.x : Boxy.DEFAULT_X,
94            Boxy._u(this.options.y) ? this.options.y : Boxy.DEFAULT_Y
95        );
96    }
97    
98    if (this.options.show) this.show();
99
100};
101
102Boxy.EF = function() {};
103
104jQuery.extend(Boxy, {
105    
106    WRAPPER:    "<table cellspacing='0' cellpadding='0' border='0' class='boxy-wrapper'>" +
107                "<tr><td class='top-left'></td><td class='top'></td><td class='top-right'></td></tr>" +
108                "<tr><td class='left'></td><td class='boxy-inner'></td><td class='right'></td></tr>" +
109                "<tr><td class='bottom-left'></td><td class='bottom'></td><td class='bottom-right'></td></tr>" +
110                "</table>",
111    
112    DEFAULTS: {
113        title:                  null,           // titlebar text. titlebar will not be visible if not set.
114        closeable:              true,           // display close link in titlebar?
115        draggable:              true,           // can this dialog be dragged?
116        clone:                  false,          // clone content prior to insertion into dialog?
117        actuator:               null,           // element which opened this dialog
118        center:                 true,           // center dialog in viewport?
119        show:                   true,           // show dialog immediately?
120        modal:                  false,          // make dialog modal?
121        fixed:                  true,           // use fixed positioning, if supported? absolute positioning used otherwise
122        closeText:              '[close]',      // text to use for default close link
123        unloadOnHide:           false,          // should this dialog be removed from the DOM after being hidden?
124        clickToFront:           false,          // bring dialog to foreground on any click (not just titlebar)?
125        behaviours:             Boxy.EF,        // function used to apply behaviours to all content embedded in dialog.
126        afterDrop:              Boxy.EF,        // callback fired after dialog is dropped. executes in context of Boxy instance.
127        afterShow:              Boxy.EF,        // callback fired after dialog becomes visible. executes in context of Boxy instance.
128        afterHide:              Boxy.EF,        // callback fired after dialog is hidden. executed in context of Boxy instance.
129        beforeUnload:           Boxy.EF         // callback fired after dialog is unloaded. executed in context of Boxy instance.
130    },
131    
132    DEFAULT_X:          50,
133    DEFAULT_Y:          50,
134    zIndex:             99090,
135    dragConfigured:     false, // only set up one drag handler for all boxys
136    resizeConfigured:   false,
137    dragging:           null,
138    
139    // load a URL and display in boxy
140    // url - url to load
141    // options keys (any not listed below are passed to boxy constructor)
142    //   type: HTTP method, default: GET
143    //   cache: cache retrieved content? default: false
144    //   filter: jQuery selector used to filter remote content
145    load: function(url, options) {
146        
147        options = options || {};
148        
149        var ajax = {
150            url: url, type: 'GET', dataType: 'html', cache: false, success: function(html) {
151                html = jQuery(html);
152                if (options.filter) html = jQuery(options.filter, html);
153                new Boxy(html, options);
154            }
155        };
156        
157        jQuery.each(['type', 'cache'], function() {
158            if (this in options) {
159                ajax[this] = options[this];
160                delete options[this];
161            }
162        });
163        
164        jQuery.ajax(ajax);
165        
166    },
167    
168    // allows you to get a handle to the containing boxy instance of any element
169    // e.g. <a href='#' onclick='alert(Boxy.get(this));'>inspect!</a>.
170    // this returns the actual instance of the boxy 'class', not just a DOM element.
171    // Boxy.get(this).hide() would be valid, for instance.
172    get: function(ele) {
173        var p = jQuery(ele).parents('.boxy-wrapper');
174        return p.length ? jQuery.data(p[0], 'boxy') : null;
175    },
176    
177    // returns the boxy instance which has been linked to a given element via the
178    // 'actuator' constructor option.
179    linkedTo: function(ele) {
180        return jQuery.data(ele, 'active.boxy');
181    },
182    
183    // displays an alert box with a given message, calling optional callback
184    // after dismissal.
185    alert: function(message, callback, options) {
186        return Boxy.ask(message, ['OK'], callback, options);
187    },
188    
189    // displays an alert box with a given message, calling after callback iff
190    // user selects OK.
191    confirm: function(message, after, options) {
192        return Boxy.ask(message, ['OK', 'Cancel'], function(response) {
193            if (response == 'OK') after();
194        }, options);
195    },
196    
197    // asks a question with multiple responses presented as buttons
198    // selected item is returned to a callback method.
199    // answers may be either an array or a hash. if it's an array, the
200    // the callback will received the selected value. if it's a hash,
201    // you'll get the corresponding key.
202    ask: function(question, answers, callback, options) {
203        
204        options = jQuery.extend({modal: true, closeable: false},
205                                options || {},
206                                {show: true, unloadOnHide: true});
207        
208        var body = jQuery('<div></div>').append(jQuery('<div class="question"></div>').html(question));
209        
210        // ick
211        var map = {}, answerStrings = [];
212        if (answers instanceof Array) {
213            for (var i = 0; i < answers.length; i++) {
214                map[answers[i]] = answers[i];
215                answerStrings.push(answers[i]);
216            }
217        } else {
218            for (var k in answers) {
219                map[answers[k]] = k;
220                answerStrings.push(answers[k]);
221            }
222        }
223        
224        var buttons = jQuery('<form class="answers"></form>');
225        buttons.html(jQuery.map(answerStrings, function(v) {
226            return "<input type='button' value='" + v + "' />";
227        }).join(' '));
228        
229        jQuery('input[type=button]', buttons).on('click', function() {
230            var clicked = this;
231            Boxy.get(this).hide(function() {
232                if (callback) callback(map[clicked.value]);
233            });
234        });
235        
236        body.append(buttons);
237        
238        new Boxy(body, options);
239        
240    },
241    
242    // returns true if a modal boxy is visible, false otherwise
243    isModalVisible: function() {
244        return jQuery('.boxy-modal-blackout').length > 0;
245    },
246    
247    _u: function() {
248        for (var i = 0; i < arguments.length; i++)
249            if (typeof arguments[i] != 'undefined') return false;
250        return true;
251    },
252    
253    _handleResize: function(evt) {
254        var d = jQuery(document);
255        jQuery('.boxy-modal-blackout').css('display', 'none').css({
256            width: d.width(), height: d.height()
257        }).css('display', 'block');
258    },
259    
260    _handleDrag: function(evt) {
261        var d;
262        if (d = Boxy.dragging) {
263            d[0].boxy.css({left: evt.pageX - d[1], top: evt.pageY - d[2]});
264        }
265    },
266    
267    _nextZ: function() {
268        return Boxy.zIndex++;
269    },
270    
271    _viewport: function() {
272        var d = document.documentElement, b = document.body, w = window;
273        return jQuery.extend(
274            // jQuery.browser.msie ?
275            //     { left: b.scrollLeft || d.scrollLeft, top: b.scrollTop || d.scrollTop } :
276                { left: w.pageXOffset, top: w.pageYOffset },
277            !Boxy._u(w.innerWidth) ?
278                { width: w.innerWidth, height: w.innerHeight } :
279                (!Boxy._u(d) && !Boxy._u(d.clientWidth) && d.clientWidth != 0 ?
280                    { width: d.clientWidth, height: d.clientHeight } :
281                    { width: b.clientWidth, height: b.clientHeight }) );
282    }
283
284});
285
286Boxy.prototype = {
287    
288    // Returns the size of this boxy instance without displaying it.
289    // Do not use this method if boxy is already visible, use getSize() instead.
290    estimateSize: function() {
291        this.boxy.css({visibility: 'hidden', display: 'block'});
292        var dims = this.getSize();
293        this.boxy.css('display', 'none').css('visibility', 'visible');
294        return dims;
295    },
296                
297    // Returns the dimensions of the entire boxy dialog as [width,height]
298    getSize: function() {
299        return [this.boxy.width(), this.boxy.height()];
300    },
301    
302    // Returns the dimensions of the content region as [width,height]
303    getContentSize: function() {
304        var c = this.getContent();
305        return [c.width(), c.height()];
306    },
307    
308    // Returns the position of this dialog as [x,y]
309    getPosition: function() {
310        var b = this.boxy[0];
311        return [b.offsetLeft, b.offsetTop];
312    },
313    
314    // Returns the center point of this dialog as [x,y]
315    getCenter: function() {
316        var p = this.getPosition();
317        var s = this.getSize();
318        return [Math.floor(p[0] + s[0] / 2), Math.floor(p[1] + s[1] / 2)];
319    },
320                
321    // Returns a jQuery object wrapping the inner boxy region.
322    // Not much reason to use this, you're probably more interested in getContent()
323    getInner: function() {
324        return jQuery('.boxy-inner', this.boxy);
325    },
326    
327    // Returns a jQuery object wrapping the boxy content region.
328    // This is the user-editable content area (i.e. excludes titlebar)
329    getContent: function() {
330        return jQuery('.boxy-content', this.boxy);
331    },
332    
333    // Replace dialog content
334    setContent: function(newContent) {
335        newContent = jQuery(newContent).css({display: 'block'}).addClass('boxy-content');
336        if (this.options.clone) newContent = newContent.clone(true);
337        this.getContent().remove();
338        this.getInner().append(newContent);
339        this._setupDefaultBehaviours(newContent);
340        this.options.behaviours.call(this, newContent);
341        return this;
342    },
343    
344    // Move this dialog to some position, funnily enough
345    moveTo: function(x, y) {
346        this.moveToX(x).moveToY(y);
347        return this;
348    },
349    
350    // Move this dialog (x-coord only)
351    moveToX: function(x) {
352        if (typeof x == 'number') this.boxy.css({left: x});
353        else this.centerX();
354        return this;
355    },
356    
357    // Move this dialog (y-coord only)
358    moveToY: function(y) {
359        if (typeof y == 'number') this.boxy.css({top: y});
360        else this.centerY();
361        return this;
362    },
363    
364    // Move this dialog so that it is centered at (x,y)
365    centerAt: function(x, y) {
366        var s = this[this.visible ? 'getSize' : 'estimateSize']();
367        if (typeof x == 'number') this.moveToX(x - s[0] / 2);
368        if (typeof y == 'number') this.moveToY(y - s[1] / 2);
369        return this;
370    },
371    
372    centerAtX: function(x) {
373        return this.centerAt(x, null);
374    },
375    
376    centerAtY: function(y) {
377        return this.centerAt(null, y);
378    },
379    
380    // Center this dialog in the viewport
381    // axis is optional, can be 'x', 'y'.
382    center: function(axis) {
383        var v = Boxy._viewport();
384        var o = this.options.fixed ? [0, 0] : [v.left, v.top];
385        if (!axis || axis == 'x') this.centerAt(o[0] + v.width / 2, null);
386        if (!axis || axis == 'y') this.centerAt(null, o[1] + v.height / 2);
387        return this;
388    },
389    
390    // Center this dialog in the viewport (x-coord only)
391    centerX: function() {
392        return this.center('x');
393    },
394    
395    // Center this dialog in the viewport (y-coord only)
396    centerY: function() {
397        return this.center('y');
398    },
399    
400    // Resize the content region to a specific size
401    resize: function(width, height, after) {
402        if (!this.visible) return;
403        var bounds = this._getBoundsForResize(width, height);
404        this.boxy.css({left: bounds[0], top: bounds[1]});
405        this.getContent().css({width: bounds[2], height: bounds[3]});
406        if (after) after(this);
407        return this;
408    },
409    
410    // Tween the content region to a specific size
411    tween: function(width, height, after) {
412        if (!this.visible) return;
413        var bounds = this._getBoundsForResize(width, height);
414        var self = this;
415        this.boxy.stop().animate({left: bounds[0], top: bounds[1]});
416        this.getContent().stop().animate({width: bounds[2], height: bounds[3]}, function() {
417            if (after) after(self);
418        });
419        return this;
420    },
421    
422    // Returns true if this dialog is visible, false otherwise
423    isVisible: function() {
424        return this.visible;    
425    },
426    
427    // Make this boxy instance visible
428    show: function() {
429        if (this.visible) return;
430        if (this.options.modal) {
431            var self = this;
432            if (!Boxy.resizeConfigured) {
433                Boxy.resizeConfigured = true;
434                jQuery(window).on("resize", function() { Boxy._handleResize(); });
435            }
436            this.modalBlackout = jQuery('<div class="boxy-modal-blackout"></div>')
437                .css({zIndex: Boxy._nextZ(),
438                      opacity: 0.7,
439                      width: jQuery(document).width(),
440                      height: jQuery(document).height()})
441                .appendTo(document.body);
442            this.toTop();
443            if (this.options.closeable) {
444                jQuery(document.body).on('keypress.boxy', function(evt) {
445                    var key = evt.which || evt.keyCode;
446                    if (key == 27) {
447                        self.hide();
448                        jQuery(document.body).off('keypress.boxy');
449                    }
450                });
451            }
452        }
453        this.boxy.stop().css({opacity: 1}).show();
454        this.visible = true;
455        this._fire('afterShow');
456        return this;
457    },
458    
459    // Hide this boxy instance
460    hide: function(after) {
461        if (!this.visible) return;
462        var self = this;
463        if (this.options.modal) {
464            jQuery(document.body).off('keypress.boxy');
465            this.modalBlackout.animate({opacity: 0}, function() {
466                jQuery(this).remove();
467            });
468        }
469        this.boxy.stop().animate({opacity: 0}, 300, function() {
470            self.boxy.css({display: 'none'});
471            self.visible = false;
472            self._fire('afterHide');
473            if (after) after(self);
474            if (self.options.unloadOnHide) self.unload();
475        });
476        return this;
477    },
478    
479    toggle: function() {
480        this[this.visible ? 'hide' : 'show']();
481        return this;
482    },
483    
484    hideAndUnload: function(after) {
485        this.options.unloadOnHide = true;
486        this.hide(after);
487        return this;
488    },
489    
490    unload: function() {
491        this._fire('beforeUnload');
492        this.boxy.remove();
493        if (this.options.actuator) {
494            jQuery.data(this.options.actuator, 'active.boxy', false);
495        }
496    },
497    
498    // Move this dialog box above all other boxy instances
499    toTop: function() {
500        this.boxy.css({zIndex: Boxy._nextZ()});
501        return this;
502    },
503    
504    // Returns the title of this dialog
505    getTitle: function() {
506        return jQuery('> .title-bar h2', this.getInner()).html();
507    },
508    
509    // Sets the title of this dialog
510    setTitle: function(t) {
511        jQuery('> .title-bar h2', this.getInner()).html(t);
512        return this;
513    },
514    
515    //
516    // Don't touch these privates
517    
518    _getBoundsForResize: function(width, height) {
519        var csize = this.getContentSize();
520        var delta = [width - csize[0], height - csize[1]];
521        var p = this.getPosition();
522        return [Math.max(p[0] - delta[0] / 2, 0),
523                Math.max(p[1] - delta[1] / 2, 0), width, height];
524    },
525    
526    _setupTitleBar: function() {
527        if (this.options.title) {
528            var self = this;
529            var tb = jQuery("<div class='title-bar'></div>").html("<h2>" + this.options.title + "</h2>");
530            if (this.options.closeable) {
531                tb.append(jQuery("<a href='#' class='close'></a>").html(this.options.closeText));
532            }
533            if (this.options.draggable) {
534                tb[0].onselectstart = function() { return false; }
535                tb[0].unselectable = 'on';
536                tb[0].style.MozUserSelect = 'none';
537                if (!Boxy.dragConfigured) {
538                    jQuery(document).mousemove(Boxy._handleDrag);
539                    Boxy.dragConfigured = true;
540                }
541                tb.mousedown(function(evt) {
542                    self.toTop();
543                    Boxy.dragging = [self, evt.pageX - self.boxy[0].offsetLeft, evt.pageY - self.boxy[0].offsetTop];
544                    jQuery(this).addClass('dragging');
545                }).mouseup(function() {
546                    jQuery(this).removeClass('dragging');
547                    Boxy.dragging = null;
548                    self._fire('afterDrop');
549                });
550            }
551            this.getInner().prepend(tb);
552            this._setupDefaultBehaviours(tb);
553        }
554    },
555    
556    _setupDefaultBehaviours: function(root) {
557        var self = this;
558        if (this.options.clickToFront) {
559            root.click(function() { self.toTop(); });
560        }
561        jQuery('.close', root).on('click',function() {
562            self.hide();
563            return false;
564        }).on('mousedown', function(evt) { evt.stopPropagation(); });
565    },
566    
567    _fire: function(event) {
568        this.options[event].call(this);
569    }
570    
571};

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.