mirror of
https://github.com/Hopiu/jquery-mobile.git
synced 2026-04-23 23:54:43 +00:00
320 lines
8.7 KiB
JavaScript
320 lines
8.7 KiB
JavaScript
/*
|
|
=* jQuery Mobile Framework : "popup" plugin
|
|
* Copyright (c) jQuery Project
|
|
* Dual licensed under the MIT or GPL Version 2 licenses.
|
|
* http://jquery.org/license
|
|
*/
|
|
|
|
(function($, undefined) {
|
|
|
|
$.widget("mobile.popup", $.mobile.widget, {
|
|
options: {
|
|
theme: null,
|
|
overlayTheme: null,
|
|
shadow: true,
|
|
corners: true,
|
|
fade: true,
|
|
transition: $.mobile.defaultDialogTransition,
|
|
initSelector: ":jqmData(role='popup')"
|
|
},
|
|
|
|
_create: function() {
|
|
var ui = {
|
|
screen : "#ui-popup-screen",
|
|
container : "#ui-popup-container"
|
|
},
|
|
proto = $(
|
|
"<div>" +
|
|
" <div id='ui-popup-screen' class='ui-selectmenu-screen ui-screen-hidden ui-popup-screen'></div>" +
|
|
" <div id='ui-popup-container' class='ui-popup-container ui-selectmenu-hidden'></div>" +
|
|
"</div>"
|
|
),
|
|
thisPage = (this.element.closest(".ui-page") || $("body")),
|
|
self = this;
|
|
|
|
// Assign the relevant parts of the proto
|
|
for (var key in ui)
|
|
ui[key] = proto.find(ui[key]).removeAttr("id");
|
|
|
|
// Apply the proto
|
|
thisPage.append(ui.screen);
|
|
ui.container.insertAfter(ui.screen);
|
|
ui.container.append(this.element);
|
|
|
|
// Define instance variables
|
|
$.extend (this, {
|
|
_ui : ui,
|
|
_isOpen : false
|
|
});
|
|
|
|
$.each (this.options, function(key) {
|
|
self._setOption(key, self.options[key], true);
|
|
});
|
|
|
|
ui.screen.bind("vclick", function(e) {
|
|
self.close();
|
|
});
|
|
},
|
|
|
|
_setTheme: function(dst, theme, unconditional) {
|
|
var classes = (dst.attr("class") || "").split(" "),
|
|
alreadyAdded = true,
|
|
currentTheme = null,
|
|
matches;
|
|
|
|
while (classes.length > 0) {
|
|
currentTheme = classes.pop();
|
|
matches = currentTheme.match(/^ui-body-([a-z])$/);
|
|
if (matches && matches.length > 1) {
|
|
currentTheme = matches[1];
|
|
break;
|
|
}
|
|
else
|
|
currentTheme = null;
|
|
}
|
|
|
|
if (theme !== currentTheme || unconditional) {
|
|
dst.removeClass("ui-body-" + currentTheme);
|
|
if (theme !== null)
|
|
dst.addClass("ui-body-" + theme);
|
|
}
|
|
},
|
|
|
|
_set_theme: function(value, unconditional) {
|
|
if (value === null)
|
|
value = "";
|
|
|
|
if (value.match(/^[a-z]$/) || value === "") {
|
|
this._setTheme(this.element, value, unconditional);
|
|
}
|
|
},
|
|
|
|
_set_overlayTheme: function(value, unconditional) {
|
|
if (value === null)
|
|
value = "";
|
|
if (value.match(/^[a-z]$/) || value === "") {
|
|
this._setTheme(this._ui.container, value, unconditional);
|
|
// The screen must always have some kind of background for fade to work, so, if the theme is being unset,
|
|
// set the background to "a".
|
|
this._setTheme(this._ui.screen, (value === "" ? "a" : value), unconditional);
|
|
}
|
|
else
|
|
console.log("Warning: " + value + " is not a valid overlay theme! Please specify a single letter a-z or an empty string!");
|
|
},
|
|
|
|
_set_shadow: function(value, unconditional) {
|
|
value = this._parseBoolean(value);
|
|
if (this._ui.container.hasClass("ui-overlay-shadow") != value || unconditional)
|
|
this._ui.container[value ? "addClass" : "removeClass"]("ui-overlay-shadow");
|
|
},
|
|
|
|
_set_corners: function(value, unconditional) {
|
|
value = this._parseBoolean(value);
|
|
if (this._ui.container.hasClass("ui-corner-all") != value || unconditional)
|
|
this._ui.container[value ? "addClass" : "removeClass"]("ui-corner-all");
|
|
},
|
|
|
|
_parseBoolean: function(value) {
|
|
if (typeof value === "boolean")
|
|
return value;
|
|
else {
|
|
value = value.toLowerCase();
|
|
return (value === "true" || value === "yes" || value === "1");
|
|
}
|
|
},
|
|
|
|
_set_fade: function(value, unconditional) {
|
|
this.options.fade = this._parseBoolean(value);
|
|
},
|
|
|
|
_set_transition: function(value, unconditional) {
|
|
if (this.options.transition != value || unconditional) {
|
|
this._ui.container
|
|
.removeClass(this.options.transition)
|
|
.addClass(value);
|
|
this.options.transition = value;
|
|
}
|
|
},
|
|
|
|
_setOption: function(key, value, unconditional) {
|
|
if (unconditional === undefined)
|
|
unconditional = false;
|
|
if (this["_set_" + key] !== undefined)
|
|
this["_set_" + key](value, unconditional);
|
|
},
|
|
|
|
_placementCoords: function(x, y) {
|
|
// Try and center the overlay over the given coordinates
|
|
var ret,
|
|
menuHeight = this._ui.container.outerHeight(true),
|
|
menuWidth = this._ui.container.outerWidth(true),
|
|
scrollTop = $( window ).scrollTop(),
|
|
screenHeight = window.innerHeight,
|
|
screenWidth = window.innerWidth,
|
|
halfheight = menuHeight / 2,
|
|
maxwidth = parseFloat( this._ui.container.css( "max-width" ) ),
|
|
roomtop = y - scrollTop,
|
|
roombot = scrollTop + screenHeight - y,
|
|
newtop, newleft;
|
|
|
|
if ( roomtop > menuHeight / 2 && roombot > menuHeight / 2 ) {
|
|
newtop = y - halfheight;
|
|
}
|
|
else {
|
|
// 30px tolerance off the edges
|
|
newtop = roomtop > roombot ? scrollTop + screenHeight - menuHeight - 30 : scrollTop + 30;
|
|
}
|
|
|
|
// If the menuwidth is smaller than the screen center is
|
|
if ( menuWidth < maxwidth ) {
|
|
newleft = ( screenWidth - menuWidth ) / 2;
|
|
}
|
|
else {
|
|
//otherwise insure a >= 30px offset from the left
|
|
newleft = x - menuWidth / 2;
|
|
|
|
// 10px tolerance off the edges
|
|
if ( newleft < 10 ) {
|
|
newleft = 10;
|
|
}
|
|
else
|
|
if ( ( newleft + menuWidth ) > screenWidth ) {
|
|
newleft = screenWidth - menuWidth - 10;
|
|
}
|
|
}
|
|
|
|
return { x : newleft, y : newtop };
|
|
},
|
|
|
|
_bindHashChange: function(){
|
|
var self = this;
|
|
$( window ).one( "hashchange.popup", function(){
|
|
self.close( true );
|
|
});
|
|
},
|
|
|
|
_unbindHashChange: function(){
|
|
$( window ).unbind( "hashchange.popup" );
|
|
},
|
|
|
|
open: function(x, y) {
|
|
if (!this._isOpen) {
|
|
var self = this,
|
|
coords = this._placementCoords(
|
|
(undefined === x ? window.innerWidth / 2 : x),
|
|
(undefined === y ? window.innerHeight / 2 : y)),
|
|
zIndexMax = 0;
|
|
|
|
$(document)
|
|
.find("*")
|
|
.each(function() {
|
|
var el = $(this),
|
|
zIndex = parseInt(el.css("z-index"));
|
|
|
|
if (!(el.is(self._ui.container) || el.is(self._ui.screen) || isNaN(zIndex)))
|
|
zIndexMax = Math.max(zIndexMax, zIndex);
|
|
});
|
|
|
|
this._ui.screen.css("z-index", (zIndexMax + 1));
|
|
this._ui.container.css("z-index", (zIndexMax + 2));
|
|
|
|
this._ui.screen
|
|
.height($(document).height())
|
|
.removeClass("ui-screen-hidden");
|
|
|
|
if (this.options.fade)
|
|
this._ui.screen.animate({opacity: 0.5}, "fast");
|
|
|
|
this._ui.container
|
|
.removeClass("ui-selectmenu-hidden")
|
|
.css({
|
|
left: coords.x,
|
|
top: coords.y
|
|
})
|
|
.addClass("in")
|
|
.animationComplete(function() {
|
|
self._ui.screen.height($(document).height());
|
|
});
|
|
|
|
// listen for hashchange that will occur when we set it to null dialog hash
|
|
$( window ).one( "hashchange", function(){
|
|
self._bindHashChange();
|
|
});
|
|
|
|
// set hash to non-linkable dialog url
|
|
$.mobile.path.set( "&ui-state=dialog" );
|
|
|
|
this._isOpen = true;
|
|
}
|
|
},
|
|
|
|
close: function( fromHash ) {
|
|
if (this._isOpen) {
|
|
var self = this,
|
|
hideScreen = function() {
|
|
self._ui.screen.addClass("ui-screen-hidden");
|
|
self._isOpen = false;
|
|
self.element.trigger("closed");
|
|
self._ui.screen.removeAttr("style");
|
|
};
|
|
|
|
this._ui.container
|
|
.removeClass("in")
|
|
.addClass("reverse out")
|
|
.animationComplete(function() {
|
|
self._ui.container
|
|
.removeClass("reverse out")
|
|
.addClass("ui-selectmenu-hidden")
|
|
.removeAttr("style");
|
|
});
|
|
|
|
if (this.options.fade)
|
|
this._ui.screen.animate({opacity: 0.0}, "fast", hideScreen);
|
|
else
|
|
hideScreen();
|
|
|
|
// unbind listener that comes with opening popup
|
|
this._unbindHashChange();
|
|
|
|
// if the close event did not come from an internal hash listener, reset URL back
|
|
if( !fromHash ){
|
|
window.history.back();
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
$(document).bind("pagecreate create", function(e) {
|
|
$($.mobile.popup.prototype.options.initSelector, e.target)
|
|
.not(":jqmData(role='none'), :jqmData(role='nojs')")
|
|
.popup();
|
|
|
|
$("a[href^='#']:jqmData(rel='popup')").each(function() {
|
|
var btn = $(this),
|
|
popup = $(btn.attr("href"));
|
|
|
|
if (popup[0]) {
|
|
// If the popup has a theme set, prevent it from being clobbered by the associated button
|
|
if ((popup.popup("option", "overlayTheme") || "").match(/[a-z]/))
|
|
popup.jqmData("overlay-theme-set", true);
|
|
btn
|
|
.attr({
|
|
"aria-haspopup": true,
|
|
"aria-owns": btn.attr("href")
|
|
})
|
|
.removeAttr("href")
|
|
.bind("vclick", function() {
|
|
// When /this/ button causes a popup, align the popup's theme with that of the button, unless the popup has a theme pre-set
|
|
if (!popup.jqmData("overlay-theme-set"))
|
|
popup.popup("option", "overlayTheme", btn.jqmData("theme"))
|
|
popup.popup("open",
|
|
btn.offset().left + btn.outerWidth() / 2,
|
|
btn.offset().top + btn.outerHeight() / 2);
|
|
});
|
|
}
|
|
else
|
|
console.log("popup menu " + btn.attr("href") + " not found.");
|
|
});
|
|
});
|
|
|
|
})(jQuery);
|