| 1 |
// $Id: drupal.js,v 1.59 2009/10/27 04:12:39 webchick Exp $
|
| 2 |
|
| 3 |
var Drupal = Drupal || { 'settings': {}, 'behaviors': {}, 'locale': {} };
|
| 4 |
|
| 5 |
// Allow other JavaScript libraries to use $.
|
| 6 |
jQuery.noConflict();
|
| 7 |
|
| 8 |
// Indicate when other scripts use $ with out wrapping their code.
|
| 9 |
if ($ === undefined) {
|
| 10 |
$ = function () {
|
| 11 |
alert('Please wrap your JavaScript code in (function ($) { ... })(jQuery); to be compatible. See http://docs.jquery.com/Using_jQuery_with_Other_Libraries.');
|
| 12 |
};
|
| 13 |
}
|
| 14 |
|
| 15 |
(function ($) {
|
| 16 |
|
| 17 |
/**
|
| 18 |
* Attach all registered behaviors to a page element.
|
| 19 |
*
|
| 20 |
* Behaviors are event-triggered actions that attach to page elements, enhancing
|
| 21 |
* default non-Javascript UIs. Behaviors are registered in the Drupal.behaviors
|
| 22 |
* object using the method 'attach' and optionally also 'detach' as follows:
|
| 23 |
* @code
|
| 24 |
* Drupal.behaviors.behaviorName = {
|
| 25 |
* attach: function (context, settings) {
|
| 26 |
* ...
|
| 27 |
* },
|
| 28 |
* detach: function (context, settings, trigger) {
|
| 29 |
* ...
|
| 30 |
* }
|
| 31 |
* };
|
| 32 |
* @endcode
|
| 33 |
*
|
| 34 |
* Drupal.attachBehaviors is added below to the jQuery ready event and so
|
| 35 |
* runs on initial page load. Developers implementing AHAH/AJAX in their
|
| 36 |
* solutions should also call this function after new page content has been
|
| 37 |
* loaded, feeding in an element to be processed, in order to attach all
|
| 38 |
* behaviors to the new content.
|
| 39 |
*
|
| 40 |
* Behaviors should use
|
| 41 |
* @code
|
| 42 |
* $(selector).once('behavior-name', function () {
|
| 43 |
* ...
|
| 44 |
* });
|
| 45 |
* @endcode
|
| 46 |
* to ensure the behavior is attached only once to a given element. (Doing so
|
| 47 |
* enables the reprocessing of given elements, which may be needed on occasion
|
| 48 |
* despite the ability to limit behavior attachment to a particular element.)
|
| 49 |
*
|
| 50 |
* @param context
|
| 51 |
* An element to attach behaviors to. If none is given, the document element
|
| 52 |
* is used.
|
| 53 |
* @param settings
|
| 54 |
* An object containing settings for the current context. If none given, the
|
| 55 |
* global Drupal.settings object is used.
|
| 56 |
*/
|
| 57 |
Drupal.attachBehaviors = function (context, settings) {
|
| 58 |
context = context || document;
|
| 59 |
settings = settings || Drupal.settings;
|
| 60 |
// Execute all of them.
|
| 61 |
$.each(Drupal.behaviors, function () {
|
| 62 |
if ($.isFunction(this.attach)) {
|
| 63 |
this.attach(context, settings);
|
| 64 |
}
|
| 65 |
});
|
| 66 |
};
|
| 67 |
|
| 68 |
/**
|
| 69 |
* Detach registered behaviors from a page element.
|
| 70 |
*
|
| 71 |
* Developers implementing AHAH/AJAX in their solutions should call this
|
| 72 |
* function before page content is about to be removed, feeding in an element
|
| 73 |
* to be processed, in order to allow special behaviors to detach from the
|
| 74 |
* content.
|
| 75 |
*
|
| 76 |
* Such implementations should look for the class name that was added in their
|
| 77 |
* corresponding Drupal.behaviors.behaviorName.attach implementation, i.e.
|
| 78 |
* behaviorName-processed, to ensure the behavior is detached only from
|
| 79 |
* previously processed elements.
|
| 80 |
*
|
| 81 |
* @param context
|
| 82 |
* An element to detach behaviors from. If none is given, the document element
|
| 83 |
* is used.
|
| 84 |
* @param settings
|
| 85 |
* An object containing settings for the current context. If none given, the
|
| 86 |
* global Drupal.settings object is used.
|
| 87 |
* @param trigger
|
| 88 |
* A string containing what's causing the behaviors to be detached. The
|
| 89 |
* possible triggers are:
|
| 90 |
* - unload: (default) The context element is being removed from the DOM.
|
| 91 |
* - move: The element is about to be moved within the DOM (for example,
|
| 92 |
* during a tabledrag row swap). After the move is completed,
|
| 93 |
* Drupal.attachBehaviors() is called, so that the behavior can undo
|
| 94 |
* whatever it did in response to the move. Many behaviors won't need to
|
| 95 |
* do anything simply in response to the element being moved, but because
|
| 96 |
* IFRAME elements reload their "src" when being moved within the DOM,
|
| 97 |
* behaviors bound to IFRAME elements (like WYSIWYG editors) may need to
|
| 98 |
* take some action.
|
| 99 |
* - serialize: When an AJAX form is submitted, this is called with the
|
| 100 |
* form as the context. This provides every behavior within the form an
|
| 101 |
* opportunity to ensure that the field elements have correct content
|
| 102 |
* in them before the form is serialized. The canonical use-case is so
|
| 103 |
* that WYSIWYG editors can update the hidden textarea to which they are
|
| 104 |
* bound.
|
| 105 |
*
|
| 106 |
* @see Drupal.attachBehaviors
|
| 107 |
*/
|
| 108 |
Drupal.detachBehaviors = function (context, settings, trigger) {
|
| 109 |
context = context || document;
|
| 110 |
settings = settings || Drupal.settings;
|
| 111 |
trigger = trigger || 'unload';
|
| 112 |
// Execute all of them.
|
| 113 |
$.each(Drupal.behaviors, function () {
|
| 114 |
if ($.isFunction(this.detach)) {
|
| 115 |
this.detach(context, settings, trigger);
|
| 116 |
}
|
| 117 |
});
|
| 118 |
};
|
| 119 |
|
| 120 |
/**
|
| 121 |
* Encode special characters in a plain-text string for display as HTML.
|
| 122 |
*/
|
| 123 |
Drupal.checkPlain = function (str) {
|
| 124 |
str = String(str);
|
| 125 |
var replace = { '&': '&', '"': '"', '<': '<', '>': '>' };
|
| 126 |
for (var character in replace) {
|
| 127 |
var regex = new RegExp(character, 'g');
|
| 128 |
str = str.replace(regex, replace[character]);
|
| 129 |
}
|
| 130 |
return str;
|
| 131 |
};
|
| 132 |
|
| 133 |
/**
|
| 134 |
* Translate strings to the page language or a given language.
|
| 135 |
*
|
| 136 |
* See the documentation of the server-side t() function for further details.
|
| 137 |
*
|
| 138 |
* @param str
|
| 139 |
* A string containing the English string to translate.
|
| 140 |
* @param args
|
| 141 |
* An object of replacements pairs to make after translation. Incidences
|
| 142 |
* of any key in this array are replaced with the corresponding value.
|
| 143 |
* Based on the first character of the key, the value is escaped and/or themed:
|
| 144 |
* - !variable: inserted as is
|
| 145 |
* - @variable: escape plain text to HTML (Drupal.checkPlain)
|
| 146 |
* - %variable: escape text and theme as a placeholder for user-submitted
|
| 147 |
* content (checkPlain + Drupal.theme('placeholder'))
|
| 148 |
* @return
|
| 149 |
* The translated string.
|
| 150 |
*/
|
| 151 |
Drupal.t = function (str, args) {
|
| 152 |
// Fetch the localized version of the string.
|
| 153 |
if (Drupal.locale.strings && Drupal.locale.strings[str]) {
|
| 154 |
str = Drupal.locale.strings[str];
|
| 155 |
}
|
| 156 |
|
| 157 |
if (args) {
|
| 158 |
// Transform arguments before inserting them.
|
| 159 |
for (var key in args) {
|
| 160 |
switch (key.charAt(0)) {
|
| 161 |
// Escaped only.
|
| 162 |
case '@':
|
| 163 |
args[key] = Drupal.checkPlain(args[key]);
|
| 164 |
break;
|
| 165 |
// Pass-through.
|
| 166 |
case '!':
|
| 167 |
break;
|
| 168 |
// Escaped and placeholder.
|
| 169 |
case '%':
|
| 170 |
default:
|
| 171 |
args[key] = Drupal.theme('placeholder', args[key]);
|
| 172 |
break;
|
| 173 |
}
|
| 174 |
str = str.replace(key, args[key]);
|
| 175 |
}
|
| 176 |
}
|
| 177 |
return str;
|
| 178 |
};
|
| 179 |
|
| 180 |
/**
|
| 181 |
* Format a string containing a count of items.
|
| 182 |
*
|
| 183 |
* This function ensures that the string is pluralized correctly. Since Drupal.t() is
|
| 184 |
* called by this function, make sure not to pass already-localized strings to it.
|
| 185 |
*
|
| 186 |
* See the documentation of the server-side format_plural() function for further details.
|
| 187 |
*
|
| 188 |
* @param count
|
| 189 |
* The item count to display.
|
| 190 |
* @param singular
|
| 191 |
* The string for the singular case. Please make sure it is clear this is
|
| 192 |
* singular, to ease translation (e.g. use "1 new comment" instead of "1 new").
|
| 193 |
* Do not use @count in the singular string.
|
| 194 |
* @param plural
|
| 195 |
* The string for the plural case. Please make sure it is clear this is plural,
|
| 196 |
* to ease translation. Use @count in place of the item count, as in "@count
|
| 197 |
* new comments".
|
| 198 |
* @param args
|
| 199 |
* An object of replacements pairs to make after translation. Incidences
|
| 200 |
* of any key in this array are replaced with the corresponding value.
|
| 201 |
* Based on the first character of the key, the value is escaped and/or themed:
|
| 202 |
* - !variable: inserted as is
|
| 203 |
* - @variable: escape plain text to HTML (Drupal.checkPlain)
|
| 204 |
* - %variable: escape text and theme as a placeholder for user-submitted
|
| 205 |
* content (checkPlain + Drupal.theme('placeholder'))
|
| 206 |
* Note that you do not need to include @count in this array.
|
| 207 |
* This replacement is done automatically for the plural case.
|
| 208 |
* @return
|
| 209 |
* A translated string.
|
| 210 |
*/
|
| 211 |
Drupal.formatPlural = function (count, singular, plural, args) {
|
| 212 |
var args = args || {};
|
| 213 |
args['@count'] = count;
|
| 214 |
// Determine the index of the plural form.
|
| 215 |
var index = Drupal.locale.pluralFormula ? Drupal.locale.pluralFormula(args['@count']) : ((args['@count'] == 1) ? 0 : 1);
|
| 216 |
|
| 217 |
if (index == 0) {
|
| 218 |
return Drupal.t(singular, args);
|
| 219 |
}
|
| 220 |
else if (index == 1) {
|
| 221 |
return Drupal.t(plural, args);
|
| 222 |
}
|
| 223 |
else {
|
| 224 |
args['@count[' + index + ']'] = args['@count'];
|
| 225 |
delete args['@count'];
|
| 226 |
return Drupal.t(plural.replace('@count', '@count[' + index + ']'));
|
| 227 |
}
|
| 228 |
};
|
| 229 |
|
| 230 |
/**
|
| 231 |
* Generate the themed representation of a Drupal object.
|
| 232 |
*
|
| 233 |
* All requests for themed output must go through this function. It examines
|
| 234 |
* the request and routes it to the appropriate theme function. If the current
|
| 235 |
* theme does not provide an override function, the generic theme function is
|
| 236 |
* called.
|
| 237 |
*
|
| 238 |
* For example, to retrieve the HTML that is output by theme_placeholder(text),
|
| 239 |
* call Drupal.theme('placeholder', text).
|
| 240 |
*
|
| 241 |
* @param func
|
| 242 |
* The name of the theme function to call.
|
| 243 |
* @param ...
|
| 244 |
* Additional arguments to pass along to the theme function.
|
| 245 |
* @return
|
| 246 |
* Any data the theme function returns. This could be a plain HTML string,
|
| 247 |
* but also a complex object.
|
| 248 |
*/
|
| 249 |
Drupal.theme = function (func) {
|
| 250 |
for (var i = 1, args = []; i < arguments.length; i++) {
|
| 251 |
args.push(arguments[i]);
|
| 252 |
}
|
| 253 |
|
| 254 |
return (Drupal.theme[func] || Drupal.theme.prototype[func]).apply(this, args);
|
| 255 |
};
|
| 256 |
|
| 257 |
/**
|
| 258 |
* Parse a JSON response.
|
| 259 |
*
|
| 260 |
* The result is either the JSON object, or an object with 'status' 0 and 'data' an error message.
|
| 261 |
*/
|
| 262 |
Drupal.parseJson = function (data) {
|
| 263 |
if ((data.substring(0, 1) != '{') && (data.substring(0, 1) != '[')) {
|
| 264 |
return { status: 0, data: data.length ? data : Drupal.t('Unspecified error') };
|
| 265 |
}
|
| 266 |
return eval('(' + data + ');');
|
| 267 |
};
|
| 268 |
|
| 269 |
/**
|
| 270 |
* Freeze the current body height (as minimum height). Used to prevent
|
| 271 |
* unnecessary upwards scrolling when doing DOM manipulations.
|
| 272 |
*/
|
| 273 |
Drupal.freezeHeight = function () {
|
| 274 |
Drupal.unfreezeHeight();
|
| 275 |
$('<div id="freeze-height"></div>').css({
|
| 276 |
position: 'absolute',
|
| 277 |
top: '0px',
|
| 278 |
left: '0px',
|
| 279 |
width: '1px',
|
| 280 |
height: $('body').css('height')
|
| 281 |
}).appendTo('body');
|
| 282 |
};
|
| 283 |
|
| 284 |
/**
|
| 285 |
* Unfreeze the body height.
|
| 286 |
*/
|
| 287 |
Drupal.unfreezeHeight = function () {
|
| 288 |
$('#freeze-height').remove();
|
| 289 |
};
|
| 290 |
|
| 291 |
/**
|
| 292 |
* Wrapper around encodeURIComponent() which avoids Apache quirks (equivalent of
|
| 293 |
* drupal_encode_path() in PHP). This function should only be used on paths, not
|
| 294 |
* on query string arguments.
|
| 295 |
*/
|
| 296 |
Drupal.encodePath = function (item, uri) {
|
| 297 |
uri = uri || location.href;
|
| 298 |
item = encodeURIComponent(item).replace(/%2F/g, '/');
|
| 299 |
return (uri.indexOf('?q=') != -1) ? item : item.replace(/%26/g, '%2526').replace(/%23/g, '%2523').replace(/\/\//g, '/%252F');
|
| 300 |
};
|
| 301 |
|
| 302 |
/**
|
| 303 |
* Get the text selection in a textarea.
|
| 304 |
*/
|
| 305 |
Drupal.getSelection = function (element) {
|
| 306 |
if (typeof element.selectionStart != 'number' && document.selection) {
|
| 307 |
// The current selection.
|
| 308 |
var range1 = document.selection.createRange();
|
| 309 |
var range2 = range1.duplicate();
|
| 310 |
// Select all text.
|
| 311 |
range2.moveToElementText(element);
|
| 312 |
// Now move 'dummy' end point to end point of original range.
|
| 313 |
range2.setEndPoint('EndToEnd', range1);
|
| 314 |
// Now we can calculate start and end points.
|
| 315 |
var start = range2.text.length - range1.text.length;
|
| 316 |
var end = start + range1.text.length;
|
| 317 |
return { 'start': start, 'end': end };
|
| 318 |
}
|
| 319 |
return { 'start': element.selectionStart, 'end': element.selectionEnd };
|
| 320 |
};
|
| 321 |
|
| 322 |
/**
|
| 323 |
* Build an error message from an AJAX response.
|
| 324 |
*/
|
| 325 |
Drupal.ajaxError = function (xmlhttp, uri) {
|
| 326 |
if (xmlhttp.status == 200 || (xmlhttp.status == 500 && xmlhttp.statusText == 'Service unavailable (with message)')) {
|
| 327 |
if ($.trim(xmlhttp.responseText)) {
|
| 328 |
var message = Drupal.t("An error occurred. \nPath: @uri\nMessage: !text", { '@uri': uri, '!text': xmlhttp.responseText });
|
| 329 |
}
|
| 330 |
else {
|
| 331 |
var message = Drupal.t("An error occurred. \nPath: @uri\n(no information available).", {'@uri': uri });
|
| 332 |
}
|
| 333 |
}
|
| 334 |
else {
|
| 335 |
var message = Drupal.t("An HTTP error @status occurred. \nPath: @uri", { '@uri': uri, '@status': xmlhttp.status });
|
| 336 |
}
|
| 337 |
return message.replace(/\n/g, '<br />');
|
| 338 |
};
|
| 339 |
|
| 340 |
// Class indicating that JS is enabled; used for styling purpose.
|
| 341 |
$('html').addClass('js');
|
| 342 |
|
| 343 |
// 'js enabled' cookie.
|
| 344 |
document.cookie = 'has_js=1; path=/';
|
| 345 |
|
| 346 |
// Attach all behaviors.
|
| 347 |
$(function () {
|
| 348 |
Drupal.attachBehaviors(document, Drupal.settings);
|
| 349 |
});
|
| 350 |
|
| 351 |
/**
|
| 352 |
* The default themes.
|
| 353 |
*/
|
| 354 |
Drupal.theme.prototype = {
|
| 355 |
|
| 356 |
/**
|
| 357 |
* Formats text for emphasized display in a placeholder inside a sentence.
|
| 358 |
*
|
| 359 |
* @param str
|
| 360 |
* The text to format (plain-text).
|
| 361 |
* @return
|
| 362 |
* The formatted text (html).
|
| 363 |
*/
|
| 364 |
placeholder: function (str) {
|
| 365 |
return '<em>' + Drupal.checkPlain(str) + '</em>';
|
| 366 |
}
|
| 367 |
};
|
| 368 |
|
| 369 |
/**
|
| 370 |
* Return whether the given variable is an object.
|
| 371 |
*
|
| 372 |
* The HEAD version of jQuery (http://code.jquery.com/jquery-nightly.js)
|
| 373 |
* includes an isObject() function, so when that gets released and incorporated
|
| 374 |
* into Drupal, this can be removed.
|
| 375 |
*/
|
| 376 |
$.extend({isObject: function(value) {
|
| 377 |
return (value !== null && typeof value === 'object');
|
| 378 |
}});
|
| 379 |
|
| 380 |
})(jQuery);
|