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