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.