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>
35 * Shows other elements inside a popup window.
37 * To apply, add the attribute data-role="popupwindow" to a <div> element inside
38 * a page. Alternatively, call popupwindow()
41 * $("#mypopupwindowContent").popupwindow();
42 * where the html might be :
43 * <div id="mypopupwindowContent"></div>
45 * To trigger the popupwindow to appear, it is necessary to make a call to its
46 * 'open()' method. This is typically done by binding a function to an event
47 * emitted by an input element, such as a the clicked event emitted by a button
48 * element. The open() method takes two arguments, specifying the x and y
49 * screen coordinates of the center of the popup window.
51 * You can associate a button with a popup window like this:
52 * <div id="mypopupContent" style="display: table;" data-role="popupwindow">
54 * <tr> <td>Eenie</td> <td>Meenie</td> <td>Mynie</td> <td>Mo</td> </tr>
55 * <tr> <td>Catch-a</td> <td>Tiger</td> <td>By-the</td> <td>Toe</td> </tr>
56 * <tr> <td>If-he</td> <td>Hollers</td> <td>Let-him</td> <td>Go</td> </tr>
57 * <tr> <td>Eenie</td> <td>Meenie</td> <td>Mynie</td> <td>Mo</td> </tr>
60 * <a href="#myPopupContent" data-rel="popupwindow" data-role="button">Show popup</a>
64 * theme: String; the theme for the popupwindow contents
67 * overlayTheme: String; the theme for the popupwindow
70 * shadow: Boolean; display a shadow around the popupwindow
73 * corners: Boolean; display a shadow around the popupwindow
76 * fade: Boolean; fades the opening and closing of the popupwindow
78 * transition: String; the transition to use when opening or closing
80 * Default: $.mobile.defaultDialogTransition
83 * popupbeforeposition: triggered after a popup has completed preparations for opening, but has not yet opened
84 * popupafteropen: triggered after a popup has completely opened
85 * popupafterclose triggered when a popup has completely closed
88 (function ( $, undefined ) {
89 $.widget( "tizen.popupwindow", $.tizen.widgetex, {
100 transition: $.mobile.defaultDialogTransition,
101 initSelector: ":jqmData(role='popupwindow')"
106 screen: "#popupwindow-screen",
107 container: "#popupwindow-container"
111 _setStyle: function () {
112 var popup = this.element,
113 style = popup.attr( 'data-style' );
116 this.options.style = style;
119 popup.addClass( this.options.style );
120 popup.find( ":jqmData(role='title')" )
121 .wrapAll( "<div class='popup-title'></div>" );
122 popup.find( ":jqmData(role='text')" )
123 .wrapAll( "<div class='popup-text'></div>" );
124 popup.find( ":jqmData(role='button-bg')" )
125 .wrapAll( "<div class='popup-button-bg'></div>" );
126 popup.find( ":jqmData(role='check-bg')" )
127 .wrapAll( "<div class='popup-check-bg'></div>" );
128 popup.find( ":jqmData(role='scroller-bg')" )
129 .wrapAll( "<div class='popup-scroller-bg'></div>" );
130 popup.find( ":jqmData(role='text-bottom-bg')" )
131 .wrapAll( "<div class='popup-text-bottom-bg'></div>" );
132 popup.find( ":jqmData(role='text-left')" )
133 .wrapAll( "<div class='popup-text-left'></div>" );
134 popup.find( ":jqmData(role='text-right')" )
135 .wrapAll( "<div class='popup-text-right'></div>" );
136 popup.find( ":jqmData(role='progress-bg')" )
137 .wrapAll( "<div class='popup-progress-bg'></div>" );
140 _create: function () {
141 var thisPage = this.element.closest(":jqmData(role='page')"),
144 if ( thisPage.length === 0 ) {
145 thisPage = $("body");
148 this._ui.placeholder =
149 $( "<div><!-- placeholder for " + this.element.attr("id") + " --></div>" )
150 .css("display", "none")
151 .insertBefore( this.element );
153 thisPage.append( this._ui.screen );
154 this._ui.container.insertAfter( this._ui.screen );
155 this._ui.container.append( this.element );
159 this._isOpen = false;
161 this._ui.screen.bind( "vclick", function ( e ) {
167 destroy: function () {
168 this.element.insertBefore( this._ui.placeholder );
170 this._ui.placeholder.remove();
171 this._ui.container.remove();
172 this._ui.screen.remove();
173 this.element.triggerHandler("destroyed");
174 $.Widget.prototype.destroy.call( this );
177 _placementCoords: function ( x, y, cw, ch ) {
178 var scrollTop = $( window ).scrollTop(),
179 screenHeight = $( window ).height(),
180 screenWidth = $( window ).width(),
182 maxwidth = parseFloat( this._ui.container.css( "max-width" ) ),
183 roomtop = y - scrollTop,
184 roombot = scrollTop + screenHeight - y,
188 if ( roomtop > ch / 2 && roombot > ch / 2 ) {
189 newtop = y - halfheight;
191 newtop = roomtop > roombot ? scrollTop + screenHeight - ch - 30 : scrollTop + 30;
194 if ( cw < maxwidth ) {
195 newleft = ( screenWidth - cw ) / 2;
197 newleft = x - cw / 2;
199 if ( newleft < 10 ) {
201 } else if ( ( newleft + cw ) > screenWidth ) {
202 newleft = screenWidth - cw - 10;
206 return { x : newleft, y : newtop };
209 _setPosition: function ( x_where, y_where ) {
210 var x = ( undefined === x_where ? $( window ).width() / 2 : x_where ),
211 y = ( undefined === y_where ? $( window ).height() / 2 : y_where ),
213 ctxpopup = this.element.data("ctxpopup"),
228 popupWidth = $( window ).width() * this.options.widthRatio;
229 this._ui.container.css( "width", popupWidth );
231 if ( this._ui.container.outerWidth() > $( window ).width() ) {
232 this._ui.container.css( {"max-width" : $( window ).width() - 30} );
236 coords = this._placementCoords( x, y,
237 this._ui.container.outerWidth(),
238 this._ui.container.outerHeight() );
240 menuHeight = this._ui.container.innerHeight();
241 menuWidth = this._ui.container.innerWidth();
242 scrollTop = $( window ).scrollTop();
243 screenHeight = $( window ).height();
244 screenWidth = $( window ).width();
245 roomtop = y - scrollTop;
246 roombot = scrollTop + screenHeight - y;
247 halfheight = menuHeight / 2;
248 maxwidth = parseFloat( this._ui.container.css( "max-width" ) );
249 newtop = ( screenHeight - menuHeight ) / 2 + scrollTop;
251 if ( menuWidth < maxwidth ) {
252 newleft = ( screenWidth - menuWidth ) / 2;
254 newleft = x - menuWidth / 2;
256 if ( newleft < 30 ) {
258 } else if ( ( newleft + menuWidth ) > screenWidth ) {
259 newleft = screenWidth - menuWidth - 30;
268 this._ui.container.css({
274 open: function ( x_where, y_where ) {
278 if ( this._isOpen || this.options.disabled ) {
282 $( document ).find("*").each( function () {
284 zIndex = parseInt( el.css("z-index"), 10 );
286 if ( !( el.is( self._ui.container ) ||
287 el.is( self._ui.screen ) ||
289 zIndexMax = Math.max( zIndexMax, zIndex );
293 this._ui.screen.css( "height", "100%" )
294 .removeClass("ui-screen-hidden");
296 if ( this.options.fade ) {
297 this._ui.screen.animate( {opacity: this.options.opacity}, "fast" );
299 this._ui.screen.css( {opacity: this.options.opacity} );
302 this._setPosition( x_where, y_where );
304 this.element.trigger("popupbeforeposition");
307 .removeClass("ui-selectmenu-hidden")
309 .animationComplete( function () {
310 self.element.trigger("popupafteropen");
315 if ( !this._reflow ) {
316 this._reflow = function () {
317 if ( !self._isOpen ) {
321 self._setPosition( x_where, y_where );
324 $( window ).bind( "resize", this._reflow );
329 if ( !this._isOpen ) {
333 if ( this._reflow ) {
334 $( window ).unbind( "resize", this._reflow );
339 hideScreen = function () {
340 self._ui.screen.addClass("ui-screen-hidden");
341 self._isOpen = false;
344 this._ui.container.removeClass("in").addClass("reverse out");
346 if ( this.options.transition === "none" ) {
348 .addClass("ui-selectmenu-hidden")
349 .removeAttr("style");
350 this.element.trigger("popupafterclose");
352 this._ui.container.animationComplete( function () {
354 .removeClass("reverse out")
355 .addClass("ui-selectmenu-hidden")
356 .removeAttr("style");
357 self.element.trigger("popupafterclose");
361 if ( this.options.fade ) {
362 this._ui.screen.animate( {opacity: 0}, "fast", hideScreen );
368 _realSetTheme: function ( dst, theme ) {
369 var classes = ( dst.attr("class") || "" ).split(" "),
374 while ( classes.length > 0 ) {
375 currentTheme = classes.pop();
376 matches = currentTheme.match(/^ui-body-([a-z])$/);
378 if ( matches && matches.length > 1 ) {
379 currentTheme = matches[1];
386 dst.removeClass( "ui-body-" + currentTheme );
387 if ( ( theme || "" ).match(/[a-z]/) ) {
388 dst.addClass( "ui-body-" + theme );
392 _setTheme: function ( value ) {
393 this._realSetTheme( this.element, value );
394 this.options.theme = value;
395 this.element.attr( "data-" + ( $.mobile.ns || "" ) + "theme", value );
398 _setOverlayTheme: function ( value ) {
399 this._realSetTheme( this._ui.container, value );
400 this.options.overlayTheme = value;
401 this.element.attr( "data-" + ( $.mobile.ns || "" ) + "overlay-theme", value );
404 _setShadow: function ( value ) {
405 this.options.shadow = value;
406 this.element.attr( "data-" + ( $.mobile.ns || "" ) + "shadow", value );
407 this._ui.container[value ? "addClass" : "removeClass"]("ui-overlay-shadow");
410 _setCorners: function ( value ) {
411 this.options.corners = value;
412 this.element.attr( "data-" + ( $.mobile.ns || "" ) + "corners", value );
413 this._ui.container[value ? "addClass" : "removeClass"]("ui-corner-all");
416 _setFade: function ( value ) {
417 this.options.fade = value;
418 this.element.attr( "data-" + ( $.mobile.ns || "" ) + "fade", value );
421 _setTransition: function ( value ) {
423 .removeClass( this.options.transition || "" )
425 this.options.transition = value;
426 this.element.attr( "data-" + ( $.mobile.ns || "" ) + "transition", value );
429 _setDisabled: function ( value ) {
430 $.Widget.prototype._setOption.call( this, "disabled", value );
437 $.tizen.popupwindow.bindPopupToButton = function ( btn, popup ) {
438 if ( btn.length === 0 || popup.length === 0 ) {
442 var btnVClickHandler = function ( e ) {
443 if ( !popup.jqmData("overlay-theme-set") ) {
444 popup.popupwindow( "option", "overlayTheme", btn.jqmData("theme") );
447 popup.popupwindow( "open",
448 btn.offset().left + btn.outerWidth() / 2,
449 btn.offset().top + btn.outerHeight() / 2 );
454 if ( ( popup.popupwindow("option", "overlayTheme") || "" ).match(/[a-z]/) ) {
455 popup.jqmData( "overlay-theme-set", true );
460 "aria-haspopup": true,
461 "aria-owns": btn.attr("href")
464 .bind( "vclick", btnVClickHandler );
466 popup.bind( "destroyed", function () {
467 btn.unbind( "vclick", btnVClickHandler );
471 $( document ).bind( "pagecreate create", function ( e ) {
472 $( $.tizen.popupwindow.prototype.options.initSelector, e.target )
473 .not(":jqmData(role='none'), :jqmData(role='nojs')")
476 $( "a[href^='#']:jqmData(rel='popupwindow')", e.target ).each( function () {
477 $.tizen.popupwindow.bindPopupToButton( $( this ), $( $( this ).attr("href") ) );