/** * boxy 0.1.4 - facebook-style dialog, with frills * * (c) 2008 jason frame * licensed under the mit license (license) */ /* * jquery plugin * * options: * message: confirmation message for form submit hook (default: "please confirm:") * * any other options - e.g. 'clone' - will be passed onto the boxy constructor (or * boxy.load for ajax operations) * *在原有基础上新增“确认”、“取消”按钮的显示文字自定义功能 *如:boxy.alert(message, btnok, callback, options); *boxy.confirm(message, btnok, btncancel, after, options); */ jquery.fn.boxy = function(options) { options = options || {}; return this.each(function() { var node = this.nodename.tolowercase(), self = this; if (node == 'a') { jquery(this).click(function() { var active = boxy.linkedto(this), href = this.getattribute('href'), localoptions = jquery.extend({actuator: this, title: this.title}, options); if (active) { active.show(); } else if (href.indexof('#') >= 0) { var content = jquery(href.substr(href.indexof('#'))), newcontent = content.clone(true); content.remove(); localoptions.unloadonhide = false; new boxy(newcontent, localoptions); } else { // fall back to ajax; could do with a same-origin check if (!localoptions.cache) localoptions.unloadonhide = true; boxy.load(this.href, localoptions); } return false; }); } else if (node == 'form') { jquery(this).bind('submit.boxy', function() { boxy.confirm(options.message || '请确认:', function() { jquery(self).unbind('submit.boxy').submit(); }); return false; }); } }); }; // // boxy class function boxy(element, options) { this.boxy = jquery(boxy.wrapper); jquery.data(this.boxy[0], 'boxy', this); this.visible = false; this.options = jquery.extend({}, boxy.defaults, options || {}); if (this.options.modal) { this.options = jquery.extend(this.options, {center: true, draggable: false}); } // options.actuator == dom element that opened this boxy // association will be automatically deleted when this boxy is remove()d if (this.options.actuator) { jquery.data(this.options.actuator, 'active.boxy', this); } this.setcontent(element || "
"); this._setuptitlebar(); this.boxy.css('display', 'none').appendto(document.body); this.totop(); if (this.options.fixed) { if (jquery.browser.msie && jquery.browser.version < 7) { this.options.fixed = false; // ie6 doesn't support fixed positioning } else { this.boxy.addclass('fixed'); } } if (this.options.center && boxy._u(this.options.x, this.options.y)) { this.center(); } else { this.moveto( boxy._u(this.options.x) ? this.options.x : boxy.default_x, boxy._u(this.options.y) ? this.options.y : boxy.default_y ); } if (this.options.show) this.show(); if (this.options.background) this.background(); }; boxy.ef = function() {}; jquery.extend(boxy, { wrapper: "" + "" + "" + "" + "
", defaults: { title: null, // titlebar text. titlebar will not be visible if not set. closeable: true, // display close link in titlebar? draggable: true, // can this dialog be dragged? clone: false, // clone content prior to insertion into dialog? actuator: null, // element which opened this dialog center: true, // center dialog in viewport? show: true, // show dialog immediately? modal: false, // make dialog modal? fixed: true, // use fixed positioning, if supported? absolute positioning used otherwise closetext: '[关闭]', // text to use for default close link unloadonhide: false, // should this dialog be removed from the dom after being hidden? clicktofront: false, // bring dialog to foreground on any click (not just titlebar)? behaviours: boxy.ef, // function used to apply behaviours to all content embedded in dialog.内容区域 afterdrop: boxy.ef, // callback fired after dialog is dropped. executes in context of boxy instance. aftershow: boxy.ef, // callback fired after dialog becomes visible. executes in context of boxy instance. afterhide: boxy.ef, // callback fired after dialog is hidden. executed in context of boxy instance. beforeunload: boxy.ef, // callback fired after dialog is unloaded. executed in context of boxy instance. btnok: 'ok', btncancel: 'cancel', background: null //背景色,默认为无 }, default_x: 50, default_y: 50, zindex: 1337, dragconfigured: false, // only set up one drag handler for all boxys resizeconfigured: false, dragging: null, // load a url and display in boxy // url - url to load // options keys (any not listed below are passed to boxy constructor) // type: http method, default: get // cache: cache retrieved content? default: false // filter: jquery selector used to filter remote content load: function(url, options) { options = options || {}; var ajax = { url: url, type: 'get', datatype: 'html', cache: false, success: function(html) { html = jquery(html); if (options.filter) html = jquery(options.filter, html); new boxy(html, options); } }; jquery.each(['type', 'cache'], function() { if (this in options) { ajax[this] = options[this]; delete options[this]; } }); jquery.ajax(ajax); }, // allows you to get a handle to the containing boxy instance of any element // e.g. inspect!. // this returns the actual instance of the boxy 'class', not just a dom element. // boxy.get(this).hide() would be valid, for instance. get: function(ele) { var p = jquery(ele).parents('.boxy-wrapper'); return p.length ? jquery.data(p[0], 'boxy') : null; }, // returns the boxy instance which has been linked to a given element via the // 'actuator' constructor option. linkedto: function(ele) { return jquery.data(ele, 'active.boxy'); }, // displays an alert box with a given message, calling optional callback // after dismissal. alert: function(message, btnok, callback, options) { btnok = btnok?btnok:boxy.defaults.btnok; return boxy.ask(message, [btnok], callback, options, [btnok]); }, // displays an alert box with a given message, calling after callback iff // user selects ok. confirm: function(message, btnok, btncancel, after, options) { btnok = btnok?btnok:boxy.defaults.btnok; btncancel = btncancel?btncancel:boxy.defaults.btncancel; return boxy.ask(message, [btnok, btncancel], function(response) { if (response == btnok) after(); }, options, [btnok, btncancel]); }, // asks a question with multiple responses presented as buttons // selected item is returned to a callback method. // answers may be either an array or a hash. if it's an array, the // the callback will received the selected value. if it's a hash, // you'll get the corresponding key. ask: function(question, answers, callback, options, btns) { options = jquery.extend({modal: true, closeable: false}, options || {}, {show: true, unloadonhide: true}); var body = jquery('
').append(jquery('
').html(question)); // ick var map = {}, answerstrings = []; if (answers instanceof array) { for (var i = 0; i < answers.length; i++) { map[answers[i]] = answers[i]; answerstrings.push(answers[i]); } } else { for (var k in answers) { map[answers[k]] = k; answerstrings.push(answers[k]); } } var buttons = jquery('
'); buttons.html(jquery.map(answerstrings, function(v) { //给确认对话框的确认取消按钮添加不同的class var btn_index; if(v === btns[0]){ btn_index = 1; }else if(v === btns[1]){ btn_index = 2; }else{ btn_index = 3; } //add end. include the 'btn_index' below return ""; }).join(' ')); jquery('input[type=button]', buttons).click(function() { var clicked = this; boxy.get(this).hide(function() { if (callback) callback(map[clicked.value]); }); }); body.append(buttons); new boxy(body, options); }, // returns true if a modal boxy is visible, false otherwise ismodalvisible: function() { return jquery('.boxy-modal-blackout').length > 0; }, _u: function() { for (var i = 0; i < arguments.length; i++) if (typeof arguments[i] != 'undefined') return false; return true; }, _handleresize: function(evt) { var d = jquery(document); jquery('.boxy-modal-blackout').css('display', 'none').css({ width: d.width(), height: d.height() }).css('display', 'block'); }, _handledrag: function(evt) { var d; if (d = boxy.dragging) { d[0].boxy.css({left: evt.pagex - d[1], top: evt.pagey - d[2]}); } }, _nextz: function() { return boxy.zindex++; }, _viewport: function() { var d = document.documentelement, b = document.body, w = window; return jquery.extend( jquery.browser.msie ? { left: b.scrollleft || d.scrollleft, top: b.scrolltop || d.scrolltop } : { left: w.pagexoffset, top: w.pageyoffset }, !boxy._u(w.innerwidth) ? { width: w.innerwidth, height: w.innerheight } : (!boxy._u(d) && !boxy._u(d.clientwidth) && d.clientwidth != 0 ? { width: d.clientwidth, height: d.clientheight } : { width: b.clientwidth, height: b.clientheight }) ); } }); boxy.prototype = { // returns the size of this boxy instance without displaying it. // do not use this method if boxy is already visible, use getsize() instead. estimatesize: function() { this.boxy.css({visibility: 'hidden', display: 'block'}); var dims = this.getsize(); this.boxy.css('display', 'none').css('visibility', 'visible'); return dims; }, // returns the dimensions of the entire boxy dialog as [width,height] getsize: function() { return [this.boxy.width(), this.boxy.height()]; }, // returns the dimensions of the content region as [width,height] getcontentsize: function() { var c = this.getcontent(); return [c.width(), c.height()]; }, // returns the position of this dialog as [x,y] getposition: function() { var b = this.boxy[0]; return [b.offsetleft, b.offsettop]; }, // returns the center point of this dialog as [x,y] getcenter: function() { var p = this.getposition(); var s = this.getsize(); return [math.floor(p[0] + s[0] / 2), math.floor(p[1] + s[1] / 2)]; }, // returns a jquery object wrapping the inner boxy region. // not much reason to use this, you're probably more interested in getcontent() getinner: function() { return jquery('.boxy-inner', this.boxy); }, // returns a jquery object wrapping the boxy content region. // this is the user-editable content area (i.e. excludes titlebar) getcontent: function() { return jquery('.boxy-content', this.boxy); }, // replace dialog content setcontent: function(newcontent) { newcontent = jquery(newcontent).css({display: 'block'}).addclass('boxy-content'); if (this.options.clone) newcontent = newcontent.clone(true); this.getcontent().remove(); this.getinner().append(newcontent); this._setupdefaultbehaviours(newcontent); this.options.behaviours.call(this, newcontent); return this; }, // move this dialog to some position, funnily enough moveto: function(x, y) { this.movetox(x).movetoy(y); return this; }, // move this dialog (x-coord only) movetox: function(x) { /* 获取水平中间位置坐标开始 */ var center_x = parseint((this.boxy.eq(0).parent().width()-this.boxy.eq(0).width()-50)/2); /* 获取水平中间位置坐标结束 */ if (typeof x == 'number') this.boxy.css({left: center_x>x*2?center_x:x}); //if (typeof x == 'number') this.boxy.css({left: x}); 谷歌和火狐浏览器下第一次出现的弹出框无法居中 else this.centerx(); return this; }, // move this dialog (y-coord only) movetoy: function(y) { if (typeof y == 'number') this.boxy.css({top: y}); else this.centery(); return this; }, // move this dialog so that it is centered at (x,y) centerat: function(x, y) { var s = this[this.visible ? 'getsize' : 'estimatesize'](); if (typeof x == 'number') this.movetox(x - s[0] / 2); if (typeof y == 'number') this.movetoy(y - s[1] / 2); return this; }, centeratx: function(x) { return this.centerat(x, null); }, centeraty: function(y) { return this.centerat(null, y); }, // center this dialog in the viewport // axis is optional, can be 'x', 'y'. center: function(axis) { var v = boxy._viewport(); var o = this.options.fixed ? [0, 0] : [v.left, v.top]; if (!axis || axis == 'x') this.centerat(o[0] + v.width / 2, null); if (!axis || axis == 'y') this.centerat(null, o[1] + v.height / 2); return this; }, // center this dialog in the viewport (x-coord only) centerx: function() { return this.center('x'); }, // center this dialog in the viewport (y-coord only) centery: function() { return this.center('y'); }, // resize the content region to a specific size resize: function(width, height, after) { if (!this.visible) return; var bounds = this._getboundsforresize(width, height); this.boxy.css({left: bounds[0], top: bounds[1]}); this.getcontent().css({width: bounds[2], height: bounds[3]}); if (after) after(this); return this; }, // tween the content region to a specific size tween: function(width, height, after) { if (!this.visible) return; var bounds = this._getboundsforresize(width, height); var self = this; this.boxy.stop().animate({left: bounds[0], top: bounds[1]}); this.getcontent().stop().animate({width: bounds[2], height: bounds[3]}, function() { if (after) after(self); }); return this; }, // returns true if this dialog is visible, false otherwise isvisible: function() { return this.visible; }, // make this boxy instance visible show: function() { if (this.visible) return; if (this.options.modal) { var self = this; if (!boxy.resizeconfigured) { boxy.resizeconfigured = true; jquery(window).resize(function() { boxy._handleresize(); }); } this.modalblackout = jquery('
') .css({zindex: boxy._nextz(), opacity: 0.7, width: jquery(document).width(), height: jquery(document).height()}) .appendto(document.body); this.totop(); if (this.options.closeable) { jquery(document.body).bind('keypress.boxy', function(evt) { var key = evt.which || evt.keycode; if (key == 27) { self.hide(); jquery(document.body).unbind('keypress.boxy'); } }); } } this.boxy.stop().css({opacity: 1}).show(); this.visible = true; this._fire('aftershow'); return this; }, // hide this boxy instance hide: function(after) { if (!this.visible) return; var self = this; if (this.options.modal || this.options.background) { jquery(document.body).unbind('keypress.boxy'); this.modalblackout.animate({opacity: 0}, function() { jquery(this).remove(); }); } this.boxy.stop().animate({opacity: 0}, 300, function() { self.boxy.css({display: 'none'}); self.visible = false; self._fire('afterhide'); if (after) after(self); if (self.options.unloadonhide) self.unload(); }); return this; }, toggle: function() { this[this.visible ? 'hide' : 'show'](); return this; }, hideandunload: function(after) { this.options.unloadonhide = true; this.hide(after); return this; }, unload: function() { this._fire('beforeunload'); this.boxy.remove(); if (this.options.actuator) { jquery.data(this.options.actuator, 'active.boxy', false); } }, //背景色设置 background: function() { if (!this.visible) return; if (!this.options.modal && this.options.background) { var self = this; var classname = 'modal'; if (!boxy.resizeconfigured) { boxy.resizeconfigured = true; jquery(window).resize(function() { boxy._handleresize(); }); } switch(this.options.background){ case 'transparent': classname = 'transparent' break; default: classname = 'modal' break; } this.modalblackout = jquery('
') .css({zindex: boxy._nextz(), opacity: 0.7, width: jquery(document).width(), height: jquery(document).height()}) .appendto(document.body); this.totop(); if (this.options.closeable) { jquery(document.body).bind('keypress.boxy', function(evt) { var key = evt.which || evt.keycode; if (key == 27) { self.hide(); jquery(document.body).unbind('keypress.boxy'); } }); } } this.boxy.stop().css({opacity: 1}).show(); this.visible = true; this._fire('aftershow'); return this; }, // move this dialog box above all other boxy instances totop: function() { this.boxy.css({zindex: boxy._nextz()}); return this; }, // returns the title of this dialog gettitle: function() { return jquery('> .title-bar h2', this.getinner()).html(); }, // sets the title of this dialog settitle: function(t) { jquery('> .title-bar h2', this.getinner()).html(t); return this; }, // // don't touch these privates _getboundsforresize: function(width, height) { var csize = this.getcontentsize(); var delta = [width - csize[0], height - csize[1]]; var p = this.getposition(); return [math.max(p[0] - delta[0] / 2, 0), math.max(p[1] - delta[1] / 2, 0), width, height]; }, _setuptitlebar: function() { if (this.options.title) { var self = this; var tb = jquery("
").html("

" + this.options.title + "

"); if (this.options.closeable) { tb.append(jquery("").html(this.options.closetext)); } if (this.options.draggable) { tb[0].onselectstart = function() { return false; } tb[0].unselectable = 'on'; tb[0].style.mozuserselect = 'none'; if (!boxy.dragconfigured) { jquery(document).mousemove(boxy._handledrag); boxy.dragconfigured = true; } tb.mousedown(function(evt) { self.totop(); boxy.dragging = [self, evt.pagex - self.boxy[0].offsetleft, evt.pagey - self.boxy[0].offsettop]; jquery(this).addclass('dragging'); }).mouseup(function() { jquery(this).removeclass('dragging'); boxy.dragging = null; self._fire('afterdrop'); }); } this.getinner().prepend(tb); this._setupdefaultbehaviours(tb); } }, _setupdefaultbehaviours: function(root) { var self = this; if (this.options.clicktofront) { root.click(function() { self.totop(); }); } jquery('.close', root).click(function() { self.hide(); return false; }).mousedown(function(evt) { evt.stoppropagation(); }); }, _fire: function(event) { this.options[event].call(this); } };