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) 2011 by Intel Corporation Ltd.
10 * Permission is hereby granted, free of charge, to any person obtaining a
11 * copy of this software and associated documentation files (the "Software"),
12 * to deal in the Software without restriction, including without limitation
13 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
14 * and/or sell copies of the Software, and to permit persons to whom the
15 * Software is furnished to do so, subject to the following conditions:
17 * The above copyright notice and this permission notice shall be included in
18 * all copies or substantial portions of the Software.
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26 * DEALINGS IN THE SOFTWARE.
27 * ***************************************************************************
29 * Authors: Gabriel Schulhof <gabriel.schulhof@intel.com>
32 // This widget is implemented in an extremely ugly way. It should derive from $.tizen.popupwindow, but it doesn't
33 // because there's a bug in jquery.ui.widget.js which was fixed in jquery-ui commit
34 // b9153258b0f0edbff49496ed16d2aa93bec07d95. Once a version of jquery-ui containing that commit is released
35 // (probably >= 1.9m5), and jQuery Mobile picks up the widget from there, this widget needs to be rewritten properly.
36 // The problem is that, when a widget inherits from a superclass and declares an object in its prototype identical in key
37 // to one in the superclass, upon calling $.widget the object is overwritten in both the prototype of the superclass and
38 // the prototype of the subclass. The prototype of the superclass should remain unchanged.
40 (function($, undefined) {
41 $.widget("tizen.ctxpopup", $.tizen.widgetex, {
42 options: $.extend({}, $.tizen.popupwindow.prototype.options, {
43 initSelector: ":not(:not(" + $.tizen.popupwindow.prototype.options.initSelector + ")):not(:not(:jqmData(show-arrow='true'), :jqmData(show-arrow)))"
49 container : "#container", // the key has to have the name "container"
51 all : ":jqmData(role='triangle')",
61 if (!this.element.data("popupwindow"))
62 this.element.popupwindow();
63 this.element.data("popupwindow")
65 .removeClass("ui-popupwindow-padding")
66 .append(this._ui.outer);
67 this._ui.outer.trigger("create"); // Creates the triangle widgets
69 .addClass("ui-popupwindow-padding")
70 .append(this.element);
73 _setOption: function(key, value) {
74 $.tizen.popupwindow.prototype._setOption.apply(this.element.data("popupwindow"), arguments);
75 this.options[key] = value;
79 var origOpen = $.tizen.popupwindow.prototype.open,
80 orig_setOption = $.tizen.popupwindow.prototype._setOption,
81 orig_placementCoords = $.tizen.popupwindow.prototype._placementCoords;
83 $.tizen.popupwindow.prototype._setOption = function(key, value) {
84 var ctxpopup = this.element.data("ctxpopup"),
87 if ("shadow" === key || "overlayTheme" === key || "corners" === key) {
88 var origContainer = this._ui.container;
90 this._ui.container = ctxpopup._ui.container;
91 orig_setOption.apply(this, arguments);
92 this._ui.container = origContainer;
93 needsApplying = false;
95 ctxpopup.options[key] = value;
99 orig_setOption.apply(this, arguments);
102 $.tizen.popupwindow.prototype._placementCoords = function(x, y, cx, cy) {
103 var ctxpopup = this.element.data("ctxpopup"),
107 var coords = {}, minDiff, minDiffIdx;
112 // triangleOffset: int
113 // actual: { x: int, y: int }
115 function getCoords(arrow, x_factor, y_factor) {
116 // Unhide the arrow we want to test to take it into account
117 ctxpopup._ui.arrow.all.hide();
118 ctxpopup._ui.arrow[arrow].show();
120 var isHorizontal = ("b" === arrow || "t" === arrow),
121 // Names of keys used in calculations depend on whether things are horizontal or not
122 coord = (isHorizontal
123 ? {point: "x", size: "cx", beg: "left", outerSize: "outerWidth", niceSize: "width", triangleSize : "height"}
124 : {point: "y", size: "cy", beg: "top", outerSize: "outerHeight", niceSize: "height", triangleSize : "width"}),
126 cx : self._ui.container.width(),
127 cy : self._ui.container.height()
134 "x" : x + halfSize.cx * x_factor,
135 "y" : y + halfSize.cy * y_factor
137 orig = orig_placementCoords.call(self, desired.x, desired.y, size.cx, size.cy),
139 // The triangleOffset must be clamped to the range described below:
144 // ----+--+-,-----...
145 //lowerDiff -->____| |/ <-- possible rounded corner
146 //triangle size --> | /|
148 // ^ |\ | <-- lowest possible offset for triangle
149 // actual range of | | \|
150 // arrow offset | | |
151 // values due to | . . Payload table cell looks like
152 // possible rounded | . . a popup window, and it may have
153 // corners and arrow | . . arbitrary things like borders,
154 // triangle size - | | | shadows, and rounded corners.
155 // our clamp range | | /|
157 //triangle size --> |\ | <-- highest possible offset for triangle
159 //upperDiff --> | |\ <-- possible rounded corner
160 // ----+--+-'-----...
165 // We calculate lowerDiff and upperDiff by considering the offset and width of the payload (this.element)
166 // versus the offset and width of the element enclosing the triangle, because the payload is inside
167 // whatever decorations (such as borders, shadow, rounded corners) and thus can give a reliable indication
168 // of the thickness of the combined decorations
170 arrowBeg = ctxpopup._ui.arrow[arrow].offset()[coord.beg],
171 arrowSize = ctxpopup._ui.arrow[arrow][coord.outerSize](true),
172 payloadBeg = self.element.offset()[coord.beg],
173 payloadSize = self.element[coord.outerSize](true),
174 triangleSize = ctxpopup._ui.arrow[arrow][coord.triangleSize](),
177 triangleSize // triangle size
178 + Math.max(0, payloadBeg - arrowBeg), // lowerDiff
181 - triangleSize // triangle size
182 - Math.max(0, arrowBeg + arrowSize - (payloadBeg + payloadSize)), // upperDiff
183 arrowSize / 2 // arrow unrestricted offset
184 + desired[coord.point]
186 - halfSize[coord.size])),
187 // Triangle points here
189 "x": orig.x + ( isHorizontal ? triangleOffset : 0) + ("r" === arrow ? size.cx : 0),
190 "y": orig.y + (!isHorizontal ? triangleOffset : 0) + ("b" === arrow ? size.cy : 0)
194 triangleOffset : triangleOffset,
195 absDiff : Math.abs(x - final.x) + Math.abs(y - final.y)
199 ctxpopup._ui.arrow[arrow].hide();
205 l : getCoords("l", 1, 0),
206 r : getCoords("r", -1, 0),
207 t : getCoords("t", 0, 1),
208 b : getCoords("b", 0, -1)
211 $.each(coords, function(key, value) {
212 if (minDiff === undefined || value.absDiff < minDiff) {
213 minDiff = value.absDiff;
218 // Side-effect: show the appropriate arrow and move it to the right offset
219 ctxpopup._ui.arrow[minDiffIdx]
221 .triangle("option", "offset", coords[minDiffIdx].triangleOffset);
222 return coords[minDiffIdx].actual;
225 return orig_placementCoords.call(this, x, y, cx, cy);
228 $.tizen.popupwindow.prototype.open = function(x, y) {
229 var ctxpopup = this.element.data("ctxpopup");
232 this._setShadow(false);
233 this._setCorners(false);
234 this._setOverlayTheme(null);
235 this._setOption("overlayTheme", ctxpopup.options.overlayTheme);
236 ctxpopup._ui.arrow.all.triangle("option", "color", ctxpopup._ui.container.css("background-color"));
239 $('.ui-popupwindow').css('background', 'none');
242 origOpen.call(this, x, y);
245 //auto self-init widgets
246 $( document ).bind( "pagecreate create", function( e ){
247 var ctxpopups = $($.tizen.ctxpopup.prototype.options.initSelector, e.target);
248 $.tizen.ctxpopup.prototype.enhanceWithin( e.target );