Export 0.2.3
[platform/framework/web/web-ui-fw.git] / src / widgets / popupwindow_ctxpopup / js / jquery.mobile.tizen.ctxpopup.js
1 /*
2  * jQuery Mobile Widget @VERSION
3  *
4  * This software is licensed under the MIT licence (as defined by the OSI at
5  * http://www.opensource.org/licenses/mit-license.php)
6  *
7  * ***************************************************************************
8  * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd.
9  * Copyright (c) 2011 by Intel Corporation Ltd.
10  *
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:
17  *
18  * The above copyright notice and this permission notice shall be included in
19  * all copies or substantial portions of the Software.
20  *
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  * ***************************************************************************
29  *
30  * Authors: Gabriel Schulhof <gabriel.schulhof@intel.com>
31  *                      Hyunjung Kim <hjnim.kim@samsung.com>
32  */
33
34 /*
35  * % ContextPopup widget do not use anymore(will be deprecated, internal use only)
36  */
37 // This widget is implemented in an extremely ugly way. It should derive from $.tizen.popupwindow, but it doesn't
38 // because there's a bug in jquery.ui.widget.js which was fixed in jquery-ui commit
39 // b9153258b0f0edbff49496ed16d2aa93bec07d95. Once a version of jquery-ui containing that commit is released
40 // (probably >= 1.9m5), and jQuery Mobile picks up the widget from there, this widget needs to be rewritten properly.
41 // The problem is that, when a widget inherits from a superclass and declares an object in its prototype identical in key
42 // to one in the superclass, upon calling $.widget the object is overwritten in both the prototype of the superclass and
43 // the prototype of the subclass. The prototype of the superclass should remain unchanged.
44
45 /**
46         class ContextPopup
47                 The context pop-up widget shows a list of options and automatically optimizes its size within the screen. This widget is intended for a small list of options for a larger list, use the List widget. <br/>The context pop-up widget requires a target button, which must be clicked to open the context pop-up. In the default application theme, an arrow pointer is displayed at the top-left corner of the context pop-up widget when it is opened.<br/><br/> To add a context pop-up widget to the application, use the following code:
48
49                         // Target button
50                         <a href="#pop_3_icons" id="btn_3_icons" data-role="button" data-inline="true" data-rel="popupwindow">3 Icons</a>
51                         // Context pop-up
52                                 <div class="horizontal" id="pop_3_icons" data-role="popupwindow" data-show-arrow="true">
53                                 <ul>
54                                         <li class="icon">
55                                                 <a href="#" data-role="button" data-icon="call"></a>
56                                         </li>
57                                         <li class="icon">
58                                                 <a href="#" data-role="button" data-icon="favorite"></a>
59                                         </li>
60                                         <li class="text">
61                                                 <a href="#">Function</a>
62                                         </li>
63                                 </ul>
64                         </div>
65         The context pop-up can define callbacks for events as described in the [jQueryMobile documentation for pop-up events.][1]
66         You can use methods with the context pop-up as described in the [jQueryMobile documentation for pop-up methods.][2]
67         [1]: http://jquerymobile.com/demos/1.2.0-alpha.1/docs/pages/popup/events.html
68         [2]: http://jquerymobile.com/demos/1.2.0-alpha.1/docs/pages/popup/methods.html
69
70         @deprecated 2.0 verisons
71 */
72
73 (function ( $, undefined ) {
74         $.widget( "tizen.ctxpopup", $.tizen.widgetex, {
75                 options: $.extend( {}, $.tizen.popupwindow.prototype.options, {
76                         initSelector: ":not(:not(" + $.tizen.popupwindow.prototype.options.initSelector + ")):not(:not(:jqmData(show-arrow='true'), :jqmData(show-arrow)))"
77                 } ),
78
79                 _htmlProto: {
80                         ui: {
81                                 outer           : "#outer",
82                                 container       : "#container", // the key has to have the name "container"
83                                 arrow           : {
84                                         all             : ":jqmData(role='triangle')",
85                                         l               : "#left",
86                                         t               : "#top",
87                                         r               : "#right",
88                                         b               : "#bottom"
89                                 }
90                         }
91                 },
92
93                 _create: function () {
94                         console.warn("ctxpopup() was deprecated. use popup() instead.");
95                         if ( !this.element.data( "popupwindow" ) ) {
96                                 this.element.popupwindow();
97                         }
98
99                         this.element.data( "popupwindow" )
100                                 ._ui.container
101                                 .removeClass( "ui-popupwindow-padding" )
102                                 .append( this._ui.outer );
103                         this._ui.outer.trigger( "create" ); // Creates the triangle widgets
104                         this._ui.container
105                                 .addClass( "ui-popupwindow-padding" )
106                                 .append( this.element );
107                 },
108
109                 _setOption: function ( key, value ) {
110                         $.tizen.popupwindow.prototype._setOption.apply( this.element.data( "popupwindow" ), arguments );
111                         this.options[key] = value;
112                 }
113         } );
114
115         var origOpen = $.tizen.popupwindow.prototype.open,
116                 orig_setOption = $.tizen.popupwindow.prototype._setOption,
117                 orig_placementCoords = $.tizen.popupwindow.prototype._placementCoords;
118
119         $.tizen.popupwindow.prototype._setOption = function ( key, value ) {
120                 var ctxpopup = this.element.data( "ctxpopup" ),
121                         needsApplying = true,
122                         origContainer;
123                 if ( ctxpopup ) {
124                         if ( "shadow" === key || "overlayTheme" === key || "corners" === key ) {
125                                 origContainer = this._ui.container;
126
127                                 this._ui.container = ctxpopup._ui.container;
128                                 orig_setOption.apply( this, arguments );
129                                 this._ui.container = origContainer;
130                                 needsApplying = false;
131                         }
132                         ctxpopup.options[key] = value;
133                 }
134
135                 if ( needsApplying ) {
136                         orig_setOption.apply(this, arguments);
137                 }
138         };
139
140         $.tizen.popupwindow.prototype._placementCoords = function ( x, y, cx, cy ) {
141                 var ctxpopup = this.element.data( "ctxpopup" ),
142                         self = this,
143                         coords = {},
144                         minDiff,
145                         minDiffIdx;
146
147                 function getCoords( arrow, x_factor, y_factor ) {
148                         // Unhide the arrow we want to test to take it into account
149                         ctxpopup._ui.arrow.all.hide();
150                         ctxpopup._ui.arrow[arrow].show();
151
152                         var isHorizontal = ( "b" === arrow || "t" === arrow ),
153                         // Names of keys used in calculations depend on whether things are horizontal or not
154                                 coord = ( isHorizontal
155                                                 ? { point: "x", size: "cx", beg: "left", outerSize: "outerWidth",  niceSize: "width", triangleSize : "height" }
156                                                 : { point: "y", size: "cy", beg: "top",  outerSize: "outerHeight", niceSize: "height", triangleSize : "width" } ),
157                                 size = {
158                                         cx : self._ui.container.width(),
159                                         cy : self._ui.container.height()
160                                 },
161                                 halfSize = {
162                                         cx : size.cx / 2,
163                                         cy : size.cy / 2
164                                 },
165                                 desired = {
166                                         "x" : x + halfSize.cx * x_factor,
167                                         "y" : y + halfSize.cy * y_factor
168                                 },
169                                 orig = orig_placementCoords.call( self, desired.x, desired.y, size.cx, size.cy ),
170
171                         // The triangleOffset must be clamped to the range described below:
172                         //
173                         //                          +-------...
174                         //                          |   /\
175                         //                          |  /  \
176                         //                   ----+--+-,-----...
177                         //lowerDiff       -->____|  |/ <-- possible rounded corner
178                         //triangle size   -->    | /|
179                         //                   ____|/ |
180                         //                    ^  |\ | <-- lowest possible offset for triangle
181                         // actual range of    |  | \| 
182                         // arrow offset       |  |  | 
183                         // values due to      |  .  . Payload table cell looks like
184                         // possible rounded   |  .  . a popup window, and it may have
185                         // corners and arrow  |  .  . arbitrary things like borders,
186                         // triangle size -    |  |  | shadows, and rounded corners.
187                         // our clamp range    |  | /|
188                         //                   _v__|/ |
189                         //triangle size   -->    |\ | <-- highest possible offset for triangle
190                         //                   ____| \|
191                         //upperDiff       -->    |  |\ <-- possible rounded corner
192                         //                   ----+--+-'-----...
193                         //                          |  \  /
194                         //                          |   \/
195                         //                          +-------...
196                         //
197                         // We calculate lowerDiff and upperDiff by considering the offset and width of the payload (this.element)
198                         // versus the offset and width of the element enclosing the triangle, because the payload is inside
199                         // whatever decorations (such as borders, shadow, rounded corners) and thus can give a reliable indication
200                         // of the thickness of the combined decorations
201
202                                 arrowBeg = ctxpopup._ui.arrow[arrow].offset()[coord.beg],
203                                 arrowSize = ctxpopup._ui.arrow[arrow][coord.outerSize]( true ),
204                                 payloadBeg = self.element.offset()[coord.beg],
205                                 payloadSize = self.element[coord.outerSize]( true ),
206                                 triangleSize = ctxpopup._ui.arrow[arrow][coord.triangleSize](),
207                                 triangleOffset =
208                                         Math.max(
209                                                 triangleSize // triangle size
210                                                         + Math.max( 0, payloadBeg - arrowBeg ), // lowerDiff
211                                                 Math.min(
212                                                                 arrowSize // bottom
213                                                                         - triangleSize // triangle size
214                                                                         - Math.max( 0, arrowBeg + arrowSize - ( payloadBeg + payloadSize ) ), // upperDiff
215                                                                 arrowSize / 2 // arrow unrestricted offset
216                                                                         + desired[coord.point]
217                                                                         - orig[coord.point]
218                                                                         - halfSize[coord.size]
219                                                         )
220                                         ),
221                                         // Triangle points here
222                                 final = {
223                                         "x": orig.x + ( isHorizontal ? triangleOffset : 0) + ("r" === arrow ? size.cx : 0),
224                                         "y": orig.y + (!isHorizontal ? triangleOffset : 0) + ("b" === arrow ? size.cy : 0)
225                                 },
226                                 ret = {
227                                         actual                  : orig,
228                                         triangleOffset  : triangleOffset,
229                                         absDiff                 : Math.abs( x - final.x ) + Math.abs( y - final.y )
230                                 };
231
232                         // Hide it back
233                         ctxpopup._ui.arrow[arrow].hide();
234
235                         return ret;
236                 }
237
238                 if ( ctxpopup ) {
239                         // Returns:
240                         // {
241                         //    absDiff: int
242                         //    triangleOffset: int
243                         //    actual: { x: int, y: int }
244                         // }
245
246                         coords = {
247                                 l : getCoords( "l", 1, 0 ),
248                                 r : getCoords( "r", -1, 0 ),
249                                 t : getCoords( "t", 0, 1 ),
250                                 b : getCoords( "b", 0, -1 )
251                         };
252
253                         $.each( coords, function ( key, value ) {
254                                 if ( minDiff === undefined || value.absDiff < minDiff ) {
255                                         minDiff = value.absDiff;
256                                         minDiffIdx = key;
257                                 }
258                         } );
259
260                         // Side-effect: show the appropriate arrow and move it to the right offset
261                         ctxpopup._ui.arrow[minDiffIdx]
262                                 .show()
263                                 .triangle( "option", "offset", coords[minDiffIdx].triangleOffset );
264                         return coords[minDiffIdx].actual;
265                 }
266
267                 return orig_placementCoords.call( this, x, y, cx, cy );
268         };
269
270         $.tizen.popupwindow.prototype.open = function ( x, y ) {
271                 var ctxpopup = this.element.data( "ctxpopup" );
272
273                 if ( ctxpopup ) {
274                         this._setFade( false );
275                         this._setShadow( false );
276                         this._setCorners( false );
277                         this._setOverlayTheme( null );
278                         this._setOption( "overlayTheme", ctxpopup.options.overlayTheme );
279                         ctxpopup._ui.arrow.all.triangle( "option", "color", ctxpopup._ui.container.css( "background-color" ) );
280
281                         // temporary
282                         $( '.ui-popupwindow' ).css( 'background', 'none' );
283                 }
284
285                 origOpen.call( this, x, y, true );
286         };
287
288         //auto self-init widgets
289         $( document ).bind( "pagecreate create", function ( e ) {
290                 var ctxpopups = $( $.tizen.ctxpopup.prototype.options.initSelector, e.target );
291                 $.tizen.ctxpopup.prototype.enhanceWithin( e.target );
292         } );
293 }( jQuery ) );