2 * jQuery Mobile Widget @VERSION
4 * This software is licensed under the MIT licence (as defined by the OSI at
5 * http://www.opensource.org/licenses/mit-license.php)
7 * ***************************************************************************
8 * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd.
9 * Copyright (c) 2011 by Intel Corporation Ltd.
11 * Permission is hereby granted, free of charge, to any person obtaining a
12 * copy of this software and associated documentation files (the "Software"),
13 * to deal in the Software without restriction, including without limitation
14 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15 * and/or sell copies of the Software, and to permit persons to whom the
16 * Software is furnished to do so, subject to the following conditions:
18 * The above copyright notice and this permission notice shall be included in
19 * all copies or substantial portions of the Software.
21 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27 * DEALINGS IN THE SOFTWARE.
28 * ***************************************************************************
30 * Authors: Gabriel Schulhof <gabriel.schulhof@intel.com>,
31 * Elliot Smith <elliot.smith@intel.com>
34 // Shows other elements inside a popup window.
36 // To apply, add the attribute data-role="popupwindow" to a <div> element inside
37 // a page. Alternatively, call popupwindow()
38 // on an element, eg :
40 // $("#mypopupwindowContent").popupwindow();
41 // where the html might be :
42 // <div id="mypopupwindowContent"></div>
44 // To trigger the popupwindow to appear, it is necessary to make a call to its
45 // 'open()' method. This is typically done by binding a function to an event
46 // emitted by an input element, such as a the clicked event emitted by a button
47 // element. The open() method takes two arguments, specifying the x and y
48 // screen coordinates of the center of the popup window.
50 // You can associate a button with a popup window like this:
51 // <div id="mypopupContent" style="display: table;" data-role="popupwindow">
53 // <tr> <td>Eenie</td> <td>Meenie</td> <td>Mynie</td> <td>Mo</td> </tr>
54 // <tr> <td>Catch-a</td> <td>Tiger</td> <td>By-the</td> <td>Toe</td> </tr>
55 // <tr> <td>If-he</td> <td>Hollers</td> <td>Let-him</td> <td>Go</td> </tr>
56 // <tr> <td>Eenie</td> <td>Meenie</td> <td>Mynie</td> <td>Mo</td> </tr>
59 // <a href="#myPopupContent" data-rel="popupwindow" data-role="button">Show popup</a>
63 // theme: String; the theme for the popupwindow contents
66 // overlayTheme: String; the theme for the popupwindow
69 // shadow: Boolean; display a shadow around the popupwindow
72 // corners: Boolean; display a shadow around the popupwindow
75 // fade: Boolean; fades the opening and closing of the popupwindow
77 // transition: String; the transition to use when opening or closing
79 // Default: $.mobile.defaultDialogTransition
82 // close: Emitted when the popupwindow is closed.
84 (function ( $, undefined ) {
86 $.widget( "tizen.popupwindow", $.tizen.widgetex, {
96 transition: $.mobile.defaultDialogTransition,
97 initSelector: ":jqmData(role='popupwindow')"
102 screen: "#popupwindow-screen",
103 container: "#popupwindow-container"
107 _create: function () {
108 var thisPage = this.element.closest(":jqmData(role='page')"),
110 popup = this.element,
112 style = popup.attr( 'data-style' );
114 if (thisPage.length === 0) {
115 thisPage = $("body");
118 // Drop a placeholder into the location from which we shall rip out the popup window contents
119 this._ui.placeholder =
120 $("<div><!-- placeholder" +
121 (this.element.attr("id") === undefined
123 : " for " + this.element.attr("id")) + " --></div>")
124 .css("display", "none")
125 .insertBefore(this.element);
128 thisPage.append(this._ui.screen);
129 this._ui.container.insertAfter(this._ui.screen);
130 this._ui.container.append(this.element);
132 // Define instance variables
142 popup.addClass( o.style );
143 popup.find( ":jqmData(role='title')" )
144 .wrapAll( "<div class='popup-title'></div>" );
145 popup.find( ":jqmData(role='text')" )
146 .wrapAll( "<div class='popup-text'></div>" );
147 popup.find( ":jqmData(role='button-bg')" )
148 .wrapAll( "<div class='popup-button-bg'></div>" );
149 popup.find( ":jqmData(role='check-bg')" )
150 .wrapAll( "<div class='popup-check-bg'></div>" );
151 popup.find( ":jqmData(role='scroller-bg')" )
152 .wrapAll( "<div class='popup-scroller-bg'></div>" );
153 popup.find( ":jqmData(role='text-bottom-bg')" )
154 .wrapAll( "<div class='popup-text-bottom-bg'></div>" );
155 popup.find( ":jqmData(role='text-left')" )
156 .wrapAll( "<div class='popup-text-left'></div>" );
157 popup.find( ":jqmData(role='text-right')" )
158 .wrapAll( "<div class='popup-text-right'></div>" );
159 popup.find( ":jqmData(role='progress-bg')" )
160 .wrapAll( "<div class='popup-progress-bg'></div>" );
163 // Events on "screen" overlay
164 this._ui.screen.bind( "vclick", function (event) {
169 _realSetTheme: function (dst, theme) {
171 var classes = (dst.attr("class") || "").split(" "),
176 while (classes.length > 0) {
177 currentTheme = classes.pop();
178 matches = currentTheme.match(/^ui-body-([a-z])$/);
179 if (matches && matches.length > 1) {
180 currentTheme = matches[1];
187 dst.removeClass("ui-body-" + currentTheme);
188 if ((theme || "").match(/[a-z]/)) {
189 dst.addClass("ui-body-" + theme);
193 _setTheme: function (value) {
194 this._realSetTheme(this.element, value);
195 this.options.theme = value;
196 this.element.attr("data-" + ($.mobile.ns || "") + "theme", value);
199 _setOverlayTheme: function (value) {
200 this._realSetTheme(this._ui.container, value);
201 // The screen must always have some kind of background for fade to work, so, if the theme is being unset,
202 // set the background to "a".
203 this._realSetTheme(this._ui.screen, (value === "" ? "a" : value));
204 this.options.overlayTheme = value;
205 this.element.attr("data-" + ($.mobile.ns || "") + "overlay-theme", value);
208 _setShadow: function (value) {
209 this.options.shadow = value;
210 this.element.attr("data-" + ($.mobile.ns || "") + "shadow", value);
211 this._ui.container[value ? "addClass" : "removeClass"]("ui-overlay-shadow");
214 _setCorners: function (value) {
215 this.options.corners = value;
216 this.element.attr("data-" + ($.mobile.ns || "") + "corners", value);
217 this._ui.container[value ? "addClass" : "removeClass"]("ui-corner-all");
220 _setFade: function (value) {
221 this.options.fade = value;
222 this.element.attr("data-" + ($.mobile.ns || "") + "fade", value);
225 _setTransition: function (value) {
227 .removeClass((this.options.transition || ""))
229 this.options.transition = value;
230 this.element.attr("data-" + ($.mobile.ns || "") + "transition", value);
233 _setDisabled: function (value) {
234 $.Widget.prototype._setOption.call(this, "disabled", value);
240 _placementCoords: function (x, y, cx, cy) {
241 // Try and center the overlay over the given coordinates
243 scrollTop = $(window).scrollTop(),
244 screenHeight = $(window).height(),
245 screenWidth = $(window).width(),
247 maxwidth = parseFloat( this._ui.container.css( "max-width" ) ),
248 roomtop = y - scrollTop,
249 roombot = scrollTop + screenHeight - y,
253 if ( roomtop > cy / 2 && roombot > cy / 2 ) {
254 newtop = y - halfheight;
256 // 30px tolerance off the edges
257 newtop = roomtop > roombot ? scrollTop + screenHeight - cy - 30 : scrollTop + 30;
260 // If the menuwidth is smaller than the screen center is
261 if ( cx < maxwidth ) {
262 newleft = ( screenWidth - cx ) / 2;
264 //otherwise insure a >= 30px offset from the left
265 newleft = x - cx / 2;
267 // 10px tolerance off the edges
268 if ( newleft < 10 ) {
270 } else if ( ( newleft + cx ) > screenWidth ) {
271 newleft = screenWidth - cx - 10;
275 return { x : newleft, y : newtop };
278 destroy: function () {
279 // Put the element back where we ripped it out from
280 this.element.insertBefore(this._ui.placeholder);
283 this._ui.placeholder.remove();
284 this._ui.container.remove();
285 this._ui.screen.remove();
286 this.element.triggerHandler("destroyed");
287 $.Widget.prototype.destroy.call(this);
290 open: function (x_where, y_where) {
291 if (!(this._isOpen || this.options.disabled)) {
293 x = (undefined === x_where ? $(window).width() / 2 : x_where),
294 y = (undefined === y_where ? $(window).height() / 2 : y_where),
297 ctxpopup = this.element.data("ctxpopup"),
312 popupWidth = $(window).width() * this.options.widthRatio;
313 this._ui.container.css("width", popupWidth);
314 // If the width of the popup exceeds the width of the window, we need to limit the width here,
315 // otherwise outer{Width,Height}(true) below will happily report the unrestricted values, causing
316 // the popup to get placed wrong.
317 if (this._ui.container.outerWidth(true) > $(window).width()) {
318 this._ui.container.css({"max-width" : $(window).width() - 30});
322 coords = this._placementCoords(x, y,
323 this._ui.container.outerWidth(true),
324 this._ui.container.outerHeight(true));
330 zIndex = parseInt(el.css("z-index"), 10);
331 if (!(el.is(self._ui.container) || el.is(self._ui.screen) || isNaN(zIndex))) {
332 zIndexMax = Math.max(zIndexMax, zIndex);
337 .height($(document).height())
338 .removeClass("ui-screen-hidden");
340 if (this.options.fade) {
341 this._ui.screen.animate({opacity: 0.5}, "fast");
343 this._ui.screen.css({opacity: 0.0});
346 //Recalculate popup position
347 menuHeight = this._ui.container.innerHeight(true);
348 menuWidth = this._ui.container.innerWidth(true);
349 scrollTop = $(window).scrollTop();
350 screenHeight = $(window).height();
351 screenWidth = $(window).width();
352 roomtop = y - scrollTop;
353 roombot = scrollTop + screenHeight - y;
354 halfheight = menuHeight / 2;
355 maxwidth = parseFloat( this._ui.container.css( "max-width" ) );
356 newtop = (screenHeight - menuHeight) / 2 + scrollTop;
358 if ( menuWidth < maxwidth ) {
359 newleft = ( screenWidth - menuWidth ) / 2;
361 //otherwise insure a >= 30px offset from the left
362 newleft = x - menuWidth / 2;
364 // 30px tolerance off the edges
365 if ( newleft < 30 ) {
367 } else if ( ( newleft + menuWidth ) > screenWidth ) {
368 newleft = screenWidth - menuWidth - 30;
371 //Recalculate popup position End
378 .removeClass("ui-selectmenu-hidden")
384 .animationComplete(function () {
385 self._ui.screen.height($(document).height());
395 hideScreen = function () {
396 self._ui.screen.addClass("ui-screen-hidden");
397 self._isOpen = false;
398 self.element.trigger("closed");
403 .addClass("reverse out")
404 .animationComplete(function () {
406 .removeClass("reverse out")
407 .addClass("ui-selectmenu-hidden")
408 .removeAttr("style");
411 if (this.options.fade) {
412 this._ui.screen.animate({opacity: 0.0}, "fast", hideScreen);
420 $.tizen.popupwindow.bindPopupToButton = function (btn, popup) {
421 if (btn.length === 0 || popup.length === 0) {
425 var btnVClickHandler = function (e) {
426 // When /this/ button causes a popup, align the popup's theme with that of the button, unless the popup has a theme pre-set
427 if (!popup.jqmData("overlay-theme-set")) {
428 popup.popupwindow("option", "overlayTheme", btn.jqmData("theme"));
430 popup.popupwindow("open",
431 btn.offset().left + btn.outerWidth() / 2,
432 btn.offset().top + btn.outerHeight() / 2);
434 // Swallow event, because it might end up getting picked up by the popup window's screen handler, which
435 // will in turn cause the popup window to close - Thanks Sasha!
436 if (e.stopPropagation) {
439 if (e.preventDefault) {
444 // If the popup has a theme set, prevent it from being clobbered by the associated button
445 if ((popup.popupwindow("option", "overlayTheme") || "").match(/[a-z]/)) {
446 popup.jqmData("overlay-theme-set", true);
451 "aria-haspopup": true,
452 "aria-owns": btn.attr("href")
455 .bind("vclick", btnVClickHandler);
457 popup.bind("destroyed", function () {
458 btn.unbind("vclick", btnVClickHandler);
462 $(document).bind("pagecreate create", function (e) {
463 $($.tizen.popupwindow.prototype.options.initSelector, e.target)
464 .not(":jqmData(role='none'), :jqmData(role='nojs')")
467 $("a[href^='#']:jqmData(rel='popupwindow')", e.target).each(function () {
468 $.tizen.popupwindow.bindPopupToButton($(this), $($(this).attr("href")));