* Installer for Oracle fixes
[mediawiki.git] / resources / mediawiki / mediawiki.util.js
blobf7a67742c2af617895904c561d24f73991cc70d2
1 /*
2  * Utilities
3  */
5 (function ($, mw) {
7         mediaWiki.util = {
9                 /* Initialisation */
10                 'initialised' : false,
11                 'init' : function () {
12                         if (this.initialised === false) {
13                                 this.initialised = true;
15                                 // Set tooltipAccessKeyPrefix
16                                 if (is_opera) {
17                                         this.tooltipAccessKeyPrefix = 'shift-esc-';
18                                 } else if (is_chrome) {
19                                         this.tooltipAccessKeyPrefix = is_chrome_mac ? 'ctrl-option-' : 'alt-';
20                                 } else if (!is_safari_win && is_safari && webkit_version > 526) {
21                                         this.tooltipAccessKeyPrefix = 'ctrl-alt-';
22                                 } else if (!is_safari_win &&
23                                         (is_safari || clientPC.indexOf('mac') !== -1 || clientPC.indexOf('konqueror') !== -1)) {
24                                         this.tooltipAccessKeyPrefix = 'ctrl-';
25                                 } else if (is_ff2) {
26                                         this.tooltipAccessKeyPrefix = 'alt-shift-';
27                                 }
29                                 // Setup CheckboxShiftClick
30                                 $.fn.enableCheckboxShiftClick = function () {
31                                         var prevCheckbox = null;
32                                         var $box = this;
33                                         $box.click(function (e) {
34                                                 if (prevCheckbox !== null && e.shiftKey) {
35                                                         $box.slice(
36                                                           Math.min($box.index(prevCheckbox), $box.index(e.target)),
37                                                           Math.max($box.index(prevCheckbox), $box.index(e.target)) + 1
38                                                         ).attr({checked: e.target.checked ? 'checked' : ''});
39                                                 }
40                                                 prevCheckbox = e.target;
41                                         });
42                                         return $box;
43                                 };
45                                 // Prototype enhancements
46                                 if (typeof String.prototype.ucFirst === 'undefined') {
47                                         String.prototype.ucFirst = function () {
48                                                 return this.substr(0, 1).toUpperCase() + this.substr(1, this.length);
49                                         };
50                                 }
52                                 // Any initialisation after the DOM is ready
53                                 $(function () {
54                                 
55                                         // Enable CheckboxShiftClick
56                                         $('input[type=checkbox]:not(.noshiftselect)').enableCheckboxShiftClick();
57                                         
58                                         // Fill bodyContant var
59                                         if ($('#bodyContent').length) {
60                                                 mw.util.$content = $('#bodyContent');
61                                         } else if ($('#article').length) {
62                                                 mw.util.$content = $('#article');
63                                         } else {
64                                                 mw.util.$content = $('#content');
65                                         }
66                                 });
69                                 return true;
70                         }
71                         return false;
72                 },
74                 /* Main body */
76                 /**
77                 * Encodes the string like PHP's rawurlencode
78                 *
79                 * @param String str             string to be encoded
80                 */
81                 'rawurlencode' : function (str) {
82                         str = (str + '').toString();
83                         return encodeURIComponent(str).replace(/!/g, '%21').replace(/'/g, '%27').replace(/\(/g, '%28')
84                                 .replace(/\)/g, '%29').replace(/\*/g, '%2A').replace(/~/g, '%7E');
85                 },
87                 /**
88                 * Encode pagetitles for use in a URL
89                 * We want / and : to be included as literal characters in our title URLs
90                 * as they otherwise fatally break the title
91                 *
92                 * @param String str             string to be encoded
93                 */
94                 'wikiUrlencode' : function (str) {
95                         return this.rawurlencode(str).replace(/%20/g, '_').replace(/%3A/g, ':').replace(/%2F/g, '/');
96                 },
98                 /**
99                 * Get the full url to a pagename
100                 *
101                 * @param String str             pagename to link to
102                 */
103                 'getWikilink' : function (str) {
104                         return wgServer + wgArticlePath.replace('$1', this.wikiUrlencode(str));
105                 },
107                 /**
108                 * Check is a variable is empty. Support for strings, booleans, arrays and objects.
109                 * String "0" is considered empty. String containing only whitespace (ie. "  ") is considered not empty.
110                 *
111                 * @param Mixed v        the variable to check for empty ness
112                 */
113                 'isEmpty' : function (v) {
114                         var key;
115                         if (v === "" || v === 0 || v === "0" || v === null || v === false || typeof v === 'undefined') {
116                                 return true;
117                         }
118                         if (v.length === 0) {
119                                 return true;
120                         }
121                         if (typeof v === 'object') {
122                                 for (key in v) {
123                                         return false;
124                                 }
125                                 return true;
126                         }
127                         return false;
128                 },
131                 /**
132                 * Grabs the url parameter value for the given parameter
133                 * Returns null if not found
134                 *
135                 * @param String param   paramter name
136                 * @param String url             url to search through (optional)
137                 */
138                 'getParamValue' : function (param, url) {
139                         url = url ? url : document.location.href;
140                         var re = new RegExp('[^#]*[&?]' + param + '=([^&#]*)'); // Get last match, stop at hash
141                         var m = re.exec(url);
142                         if (m && m.length > 1) {
143                                 return decodeURIComponent(m[1]);
144                         }
145                         return null;
146                 },
148                 /**
149                 * Converts special characters to their HTML entities
150                 *
151                 * @param String                 str text to escape
152                 * @param Bool                   quotes if true escapes single and double quotes aswell (by default false)
153                 */
154                 'htmlEscape' : function (str, quotes) {
155                         str = $('<div/>').text(str).html();
156                         if (typeof quotes === 'undefined') {
157                                 quotes = false;
158                         }
159                         if (quotes === true) {
160                                 str = str.replace(/'/g, '&#039;').replace(/"/g, '&quot;');
161                         }
162                         return str;
163                 },
165                 /**
166                 * Converts HTML entities back to text
167                 *
168                 * @param String str             text to unescape
169                 */
170                 'htmlUnescape' : function (str) {
171                         return $('<div/>').html(str).text();
172                 },
174                 // Access key prefix
175                 // will be re-defined based on browser/operating system detection in mw.util.init()
176                 'tooltipAccessKeyPrefix' : 'alt-',
178                 // Regex to match accesskey tooltips
179                 'tooltipAccessKeyRegexp': /\[(ctrl-)?(alt-)?(shift-)?(esc-)?(.)\]$/,
181                 /**
182                  * Add the appropriate prefix to the accesskey shown in the tooltip.
183                  * If the nodeList parameter is given, only those nodes are updated;
184                  * otherwise, all the nodes that will probably have accesskeys by
185                  * default are updated.
186                  *
187                  * @param Mixed nodeList        jQuery object, or array of elements
188                  */
189                 'updateTooltipAccessKeys' : function (nodeList) {
190                         var $nodes;
191                         if (nodeList instanceof jQuery) {
192                                 $nodes = nodeList;
193                         } else if (nodeList) {
194                                 $nodes = $(nodeList);
195                         } else {
196                                 // Rather than scanning all links, just
197                                 $('#column-one a, #mw-head a, #mw-panel a, #p-logo a');
199                                 // these are rare enough that no such optimization is needed
200                                 this.updateTooltipAccessKeys($('input'));
201                                 this.updateTooltipAccessKeys($('label'));
202                                 return;
203                         }
205                         $nodes.each(function (i) {
206                                 var tip = $(this).attr('title');
207                                 if (!!tip && mw.util.tooltipAccessKeyRegexp.exec(tip)) {
208                                         tip = tip.replace(mw.util.tooltipAccessKeyRegexp, '[' + mw.util.tooltipAccessKeyPrefix + "$5]");
209                                         $(this).attr('title', tip);
210                                 }
211                         });
212                 },
213                 
214                 // jQuery object that refers to the page-content element
215                 // Populated by init()
216                 '$content' : null,
219                 /**
220                  * Add a link to a portlet menu on the page, such as:
221                  *
222                  * p-cactions (Content actions), p-personal (Personal tools), p-navigation (Navigation), p-tb (Toolbox)
223                  *
224                  * The first three paramters are required, others are optionals. Though
225                  * providing an id and tooltip is recommended.
226                  *
227                  * By default the new link will be added to the end of the list. To add the link before a given existing item,
228                  * pass the DOM node (document.getElementById('foobar') or the jQuery-selector ('#foobar') of that item.
229                  *
230                  * @example mw.util.addPortletLink('p-tb', 'http://mediawiki.org/', 'MediaWiki.org', 't-mworg', 'Go to MediaWiki.org ', 'm', '#t-print')
231                  *
232                  * @param String portlet        id of the target portlet ('p-cactions' or 'p-personal' etc.)
233                  * @param String href           link URL
234                  * @param String text           link text (will be automatically lowercased by CSS for p-cactions in Monobook)
235                  * @param String id                     id of the new item, should be unique and preferably have the appropriate prefix ('ca-', 'pt-', 'n-' or 't-')
236                  * @param String tooltip        text to show when hovering over the link, without accesskey suffix
237                  * @param String accesskey      accesskey to activate this link (one character, try to avoid conflicts. Use $('[accesskey=x').get() in the console to see if 'x' is already used.
238                  * @param mixed nextnode        DOM node or jQuery-selector of the item that the new item should be added before, should be another item in the same list will be ignored if not the so
239                  *
240                  * @return Node                         the DOM node of the new item (a LI element, or A element for older skins) or null
241                  */
242                 'addPortletLink' : function (portlet, href, text, id, tooltip, accesskey, nextnode) {
243                         var $link = $('<a />').attr('href', href).text(text);
244                         
245                         // Some skins don't have portlets
246                         // Just add it to the bottom of their 'sidebar' element ignoring the specified portlet target
247                         switch (skin) {
248                         case 'standard' :
249                         case 'cologneblue' :
250                                 $("#quickbar").append($link.after('<br />'));
251                                 return $link.get(0);
252                         case 'nostalgia' :
253                                 $("#searchform").before($link).before(' &#124; ');
254                                 return $link.get(0);
255                         default : // chick, modern, monobook, myskin, simple, vector...
256                                 
257                                 var $portlet = $('#' + portlet);
258                                 if ($portlet.length === 0) {
259                                         return null;
260                                 }
261                                 var $ul = $portlet.find('ul').eq(0);
262                                 if ($ul.length === 0) {
263                                         if ($portlet.find('div').length === 0) {
264                                                 $portlet.append('<ul />');
265                                         } else {
266                                                 $portlet.find('div').eq(-1).append('<ul />');
267                                         }
268                                         $ul = $portlet.find('ul').eq(0);
269                                 }
270                                 if ($ul.length === 0) {
271                                         return null;
272                                 }
273         
274                                 // unhide portlet if it was hidden before
275                                 $portlet.removeClass('emptyPortlet');
276                                 
277                                 var $item = $link.wrap('<li><span /></li>').parent().parent();
279                                 if (id) {
280                                         $item.attr('id', id);
281                                 }
282                                 if (accesskey) {
283                                         $link.attr('accesskey', accesskey);
284                                         tooltip += ' [' + accesskey + ']';
285                                 }
286                                 if (tooltip) {
287                                         $link.attr('title', tooltip);
288                                 }
289                                 if (accesskey && tooltip) {
290                                         this.updateTooltipAccessKeys($link);
291                                 }
292         
293                                 // Append using DOM-element passing
294                                 if (nextnode && nextnode.parentNode == $ul.get(0)) {
295                                         $(nextnode).before($item);
296                                 } else {
297                                         // If the jQuery selector isn't found within the <ul>, just append it at the end
298                                         if ($ul.find(nextnode).length === 0) {
299                                                 $ul.append($item);
300                                         } else {
301                                                 // Append using jQuery CSS selector
302                                                 $ul.find(nextnode).eq(0).before($item);
303                                         }
304                                 }
305         
306                                 return $item.get(0);
307                         }
308                 }
310         };
312         mediaWiki.util.init();
314 })(jQuery, mediaWiki);