1/** 2 * @file 3 * Drupal Bootstrap object. 4 */ 5 6/** 7 * All Drupal Bootstrap JavaScript APIs are contained in this namespace. 8 * 9 * @namespace 10 */ 11(function (_, $, Drupal, drupalSettings) { 12 'use strict'; 13 14 var Bootstrap = { 15 processedOnce: {}, 16 settings: drupalSettings.bootstrap || {} 17 }; 18 19 /** 20 * Wraps Drupal.checkPlain() to ensure value passed isn't empty. 21 * 22 * Encodes special characters in a plain-text string for display as HTML. 23 * 24 * @param {string} str 25 * The string to be encoded. 26 * 27 * @return {string} 28 * The encoded string. 29 * 30 * @ingroup sanitization 31 */ 32 Bootstrap.checkPlain = function (str) { 33 return str && Drupal.checkPlain(str) || ''; 34 }; 35 36 /** 37 * Creates a jQuery plugin. 38 * 39 * @param {String} id 40 * A jQuery plugin identifier located in $.fn. 41 * @param {Function} plugin 42 * A constructor function used to initialize the for the jQuery plugin. 43 * @param {Boolean} [noConflict] 44 * Flag indicating whether or not to create a ".noConflict()" helper method 45 * for the plugin. 46 */ 47 Bootstrap.createPlugin = function (id, plugin, noConflict) { 48 // Immediately return if plugin doesn't exist. 49 if ($.fn[id] !== void 0) { 50 return this.fatal('Specified jQuery plugin identifier already exists: @id. Use Drupal.bootstrap.replacePlugin() instead.', {'@id': id}); 51 } 52 53 // Immediately return if plugin isn't a function. 54 if (typeof plugin !== 'function') { 55 return this.fatal('You must provide a constructor function to create a jQuery plugin "@id": @plugin', {'@id': id, '@plugin': plugin}); 56 } 57 58 // Add a ".noConflict()" helper method. 59 this.pluginNoConflict(id, plugin, noConflict); 60 61 $.fn[id] = plugin; 62 }; 63 64 /** 65 * Diff object properties. 66 * 67 * @param {...Object} objects 68 * Two or more objects. The first object will be used to return properties 69 * values. 70 * 71 * @return {Object} 72 * Returns the properties of the first passed object that are not present 73 * in all other passed objects. 74 */ 75 Bootstrap.diffObjects = function (objects) { 76 var args = Array.prototype.slice.call(arguments); 77 return _.pick(args[0], _.difference.apply(_, _.map(args, function (obj) { 78 return Object.keys(obj); 79 }))); 80 }; 81 82 /** 83 * Map of supported events by regular expression. 84 * 85 * @type {Object<Event|MouseEvent|KeyboardEvent|TouchEvent,RegExp>} 86 */ 87 Bootstrap.eventMap = { 88 Event: /^(?:load|unload|abort|error|select|change|submit|reset|focus|blur|resize|scroll)$/, 89 MouseEvent: /^(?:click|dblclick|mouse(?:down|enter|leave|up|over|move|out))$/, 90 KeyboardEvent: /^(?:key(?:down|press|up))$/, 91 TouchEvent: /^(?:touch(?:start|end|move|cancel))$/ 92 }; 93 94 /** 95 * Extends a jQuery Plugin. 96 * 97 * @param {String} id 98 * A jQuery plugin identifier located in $.fn. 99 * @param {Function} callback 100 * A constructor function used to initialize the for the jQuery plugin. 101 * 102 * @return {Function|Boolean} 103 * The jQuery plugin constructor or FALSE if the plugin does not exist. 104 */ 105 Bootstrap.extendPlugin = function (id, callback) { 106 // Immediately return if plugin doesn't exist. 107 if (typeof $.fn[id] !== 'function') { 108 return this.fatal('Specified jQuery plugin identifier does not exist: @id', {'@id': id}); 109 } 110 111 // Immediately return if callback isn't a function. 112 if (typeof callback !== 'function') { 113 return this.fatal('You must provide a callback function to extend the jQuery plugin "@id": @callback', {'@id': id, '@callback': callback}); 114 } 115 116 // Determine existing plugin constructor. 117 var constructor = $.fn[id] && $.fn[id].Constructor || $.fn[id]; 118 var plugin = callback.apply(constructor, [this.settings]); 119 if (!$.isPlainObject(plugin)) { 120 return this.fatal('Returned value from callback is not a plain object that can be used to extend the jQuery plugin "@id": @obj', {'@obj': plugin}); 121 } 122 123 this.wrapPluginConstructor(constructor, plugin, true); 124 125 return $.fn[id]; 126 }; 127 128 Bootstrap.superWrapper = function (parent, fn) { 129 return function () { 130 var previousSuper = this.super; 131 this.super = parent; 132 var ret = fn.apply(this, arguments); 133 if (previousSuper) { 134 this.super = previousSuper; 135 } 136 else { 137 delete this.super; 138 } 139 return ret; 140 }; 141 }; 142 143 /** 144 * Provide a helper method for displaying when something is went wrong. 145 * 146 * @param {String} message 147 * The message to display. 148 * @param {Object} [args] 149 * An arguments to use in message. 150 * 151 * @return {Boolean} 152 * Always returns FALSE. 153 */
154 Bootstrap.fatal = function (message, args) { 155 if (this.settings.dev && console.warn) { 156 for (var name in args) { 157 if (args.hasOwnProperty(name) && typeof args[name] === 'object') { 158 args[name] = JSON.stringify(args[name]); 159 } 160 } 161 Drupal.throwError(new Error(Drupal.formatString(message, args))); 162 } 163 return false; 164 }; 165 166 /** 167 * Intersects object properties. 168 * 169 * @param {...Object} objects 170 * Two or more objects. The first object will be used to return properties 171 * values. 172 * 173 * @return {Object} 174 * Returns the properties of first passed object that intersects with all 175 * other passed objects. 176 */ 177 Bootstrap.intersectObjects = function (objects) { 178 var args = Array.prototype.slice.call(arguments); 179 return _.pick(args[0], _.intersection.apply(_, _.map(args, function (obj) { 180 return Object.keys(obj); 181 }))); 182 }; 183 184 /** 185 * Normalizes an object's values. 186 * 187 * @param {Object} obj 188 * The object to normalize. 189 * 190 * @return {Object} 191 * The normalized object. 192 */ 193 Bootstrap.normalizeObject = function (obj) { 194 if (!$.isPlainObject(obj)) { 195 return obj; 196 } 197 198 for (var k in obj) { 199 if (typeof obj[k] === 'string') { 200 if (obj[k] === 'true') { 201 obj[k] = true; 202 } 203 else if (obj[k] === 'false') { 204 obj[k] = false; 205 } 206 else if (obj[k].match(/^[\d-.]$/)) { 207 obj[k] = parseFloat(obj[k]); 208 } 209 } 210 else if ($.isPlainObject(obj[k])) { 211 obj[k] = Bootstrap.normalizeObject(obj[k]); 212 } 213 } 214 215 return obj; 216 }; 217 218 /** 219 * An object based once plugin (similar to jquery.once, but without the DOM). 220 * 221 * @param {String} id 222 * A unique identifier. 223 * @param {Function} callback 224 * The callback to invoke if the identifier has not yet been seen. 225 * 226 * @return {Bootstrap} 227 */ 228 Bootstrap.once = function (id, callback) { 229 // Immediately return if identifier has already been processed. 230 if (this.processedOnce[id]) { 231 return this; 232 } 233 callback.call(this, this.settings); 234 this.processedOnce[id] = true; 235 return this; 236 }; 237 238 /** 239 * Provide jQuery UI like ability to get/set options for Bootstrap plugins. 240 * 241 * @param {string|object} key 242 * A string value of the option to set, can be dot like to a nested key. 243 * An object of key/value pairs. 244 * @param {*} [value] 245 * (optional) A value to set for key. 246 * 247 * @returns {*} 248 * - Returns nothing if key is an object or both key and value parameters 249 * were provided to set an option. 250 * - Returns the a value for a specific setting if key was provided. 251 * - Returns an object of key/value pairs of all the options if no key or 252 * value parameter was provided. 253 * 254 * @see https://github.com/jquery/jquery-ui/blob/master/ui/widget.js 255 */ 256 Bootstrap.option = function (key, value) { 257 var options = $.isPlainObject(key) ? $.extend({}, key) : {}; 258 259 // Get all options (clone so it doesn't reference the internal object). 260 if (arguments.length === 0) { 261 return $.extend({}, this.options); 262 } 263 264 // Get/set single option. 265 if (typeof key === "string") { 266 // Handle nested keys in dot notation. 267 // e.g., "foo.bar" => { foo: { bar: true } } 268 var parts = key.split('.'); 269 key = parts.shift(); 270 var obj = options; 271 if (parts.length) { 272 for (var i = 0; i < parts.length - 1; i++) { 273 obj[parts[i]] = obj[parts[i]] || {}; 274 obj = obj[parts[i]]; 275 } 276 key = parts.pop(); 277 } 278 279 // Get. 280 if (arguments.length === 1) { 281 return obj[key] === void 0 ? null : obj[key]; 282 } 283 284 // Set. 285 obj[key] = value; 286 } 287 288 // Set multiple options. 289 $.extend(true, this.options, options); 290 }; 291 292 /** 293 * Adds a ".noConflict()" helper method if needed. 294 * 295 * @param {String} id 296 * A jQuery plugin identifier located in $.fn. 297 * @param {Function} plugin 298 * @param {Function} plugin 299 * A constructor function used to initialize the for the jQuery plugin. 300 * @param {Boolean} [noConflict]
301 * Flag indicating whether or not to create a ".noConflict()" helper method 302 * for the plugin. 303 */ 304 Bootstrap.pluginNoConflict = function (id, plugin, noConflict) { 305 if (plugin.noConflict === void 0 && (noConflict === void 0 || noConflict)) { 306 var old = $.fn[id]; 307 plugin.noConflict = function () { 308 $.fn[id] = old; 309 return this; 310 }; 311 } 312 }; 313 314 /** 315 * Replaces a Bootstrap jQuery plugin definition. 316 * 317 * @param {String} id 318 * A jQuery plugin identifier located in $.fn. 319 * @param {Function} callback 320 * A callback function that is immediately invoked and must return a 321 * function that will be used as the plugin constructor. 322 * @param {Boolean} [noConflict] 323 * Flag indicating whether or not to create a ".noConflict()" helper method 324 * for the plugin. 325 */ 326 Bootstrap.replacePlugin = function (id, callback, noConflict) { 327 // Immediately return if plugin doesn't exist. 328 if (typeof $.fn[id] !== 'function') { 329 return this.fatal('Specified jQuery plugin identifier does not exist: @id', {'@id': id}); 330 } 331 332 // Immediately return if callback isn't a function. 333 if (typeof callback !== 'function') { 334 return this.fatal('You must provide a valid callback function to replace a jQuery plugin: @callback', {'@callback': callback}); 335 } 336 337 // Determine existing plugin constructor. 338 var constructor = $.fn[id] && $.fn[id].Constructor || $.fn[id]; 339 var plugin = callback.apply(constructor, [this.settings]); 340 341 // Immediately return if plugin isn't a function. 342 if (typeof plugin !== 'function') { 343 return this.fatal('Returned value from callback is not a usable function to replace a jQuery plugin "@id": @plugin', {'@id': id, '@plugin': plugin}); 344 } 345 346 this.wrapPluginConstructor(constructor, plugin); 347 348 // Add a ".noConflict()" helper method. 349 this.pluginNoConflict(id, plugin, noConflict); 350 351 $.fn[id] = plugin; 352 }; 353 354 /** 355 * Simulates a native event on an element in the browser. 356 * 357 * Note: This is a fairly complete modern implementation. If things aren't 358 * working quite the way you intend (in older browsers), you may wish to use 359 * the jQuery.simulate plugin. If it's available, this method will defer to 360 * that plugin. 361 * 362 * @see https://github.com/jquery/jquery-simulate 363 * 364 * @param {HTMLElement|jQuery} element 365 * A DOM element to dispatch event on. Note: this may be a jQuery object, 366 * however be aware that this will trigger the same event for each element 367 * inside the jQuery collection; use with caution. 368 * @param {String|String[]} type 369 * The type(s) of event to simulate. 370 * @param {Object} [options] 371 * An object of options to pass to the event constructor. Typically, if 372 * an event is being proxied, you should just pass the original event 373 * object here. This allows, if the browser supports it, to be a truly 374 * simulated event. 375 * 376 * @return {Boolean} 377 * The return value is false if event is cancelable and at least one of the 378 * event handlers which handled this event called Event.preventDefault(). 379 * Otherwise it returns true. 380 */ 381 Bootstrap.simulate = function (element, type, options) { 382 // Handle jQuery object wrappers so it triggers on each element. 383 var ret = true; 384 if (element instanceof $) { 385 element.each(function () { 386 if (!Bootstrap.simulate(this, type, options)) { 387 ret = false;
388 } 389 }); 390 return ret; 391 } 392 393 if (!(element instanceof HTMLElement)) { 394 this.fatal('Passed element must be an instance of HTMLElement, got "@type" instead.', { 395 '@type': typeof element, 396 }); 397 } 398 399 // Defer to the jQuery.simulate plugin, if it's available. 400 if (typeof $.simulate === 'function') { 401 new $.simulate(element, type, options); 402 return true; 403 } 404 405 var event; 406 var ctor; 407 var types = [].concat(type); 408 for (var i = 0, l = types.length; i < l; i++) { 409 type = types[i]; 410 for (var name in this.eventMap) { 411 if (this.eventMap[name].test(type)) { 412 ctor = name; 413 break; 414 } 415 } 416 if (!ctor) { 417 throw new SyntaxError('Only rudimentary HTMLEvents, KeyboardEvents and MouseEvents are supported: ' + type); 418 } 419 var opts = {bubbles: true, cancelable: true}; 420 if (ctor === 'KeyboardEvent' || ctor === 'MouseEvent') { 421 $.extend(opts, {ctrlKey: !1, altKey: !1, shiftKey: !1, metaKey: !1}); 422 } 423 if (ctor === 'MouseEvent') { 424 $.extend(opts, {button: 0, pointerX: 0, pointerY: 0, view: window}); 425 } 426 if (options) { 427 $.extend(opts, options); 428 } 429 if (typeof window[ctor] === 'function') { 430 event = new window[ctor](type, opts); 431 if (!element.dispatchEvent(event)) { 432 ret = false;
433 } 434 } 435 else if (document.createEvent) { 436 event = document.createEvent(ctor); 437 event.initEvent(type, opts.bubbles, opts.cancelable); 438 if (!element.dispatchEvent(event)) { 439 ret = false; 440 } 441 } 442 else if (typeof element.fireEvent === 'function') { 443 event = $.extend(document.createEventObject(), opts); 444 if (!element.fireEvent('on' + type, event)) { 445 ret = false; 446 } 447 } 448 else if (typeof element[type]) { 449 element[type](); 450 } 451 } 452 return ret; 453 }; 454 455 /** 456 * Strips HTML and returns just text. 457 * 458 * @param {String|Element|jQuery} html 459 * A string of HTML content, an Element DOM object or a jQuery object. 460 * 461 * @return {String} 462 * The text without HTML tags. 463 * 464 * @todo Replace with http://locutus.io/php/strings/strip_tags/ 465 */ 466 Bootstrap.stripHtml = function (html) { 467 if (html instanceof $) { 468 html = html.html(); 469 } 470 else if (html instanceof Element) { 471 html = html.innerHTML; 472 } 473 var tmp = document.createElement('DIV'); 474 tmp.innerHTML = html; 475 return (tmp.textContent || tmp.innerText || '').replace(/^[\s\n\t]*|[\s\n\t]*$/, ''); 476 }; 477 478 /** 479 * Provide a helper method for displaying when something is unsupported. 480 * 481 * @param {String} type 482 * The type of unsupported object, e.g. method or option. 483 * @param {String} name 484 * The name of the unsupported object. 485 * @param {*} [value] 486 * The value of the unsupported object. 487 */ 488 Bootstrap.unsupported = function (type, name, value) { 489 Bootstrap.warn('Unsupported by Drupal Bootstrap: (@type) @name -> @value', { 490 '@type': type, 491 '@name': name, 492 '@value': typeof value === 'object' ? JSON.stringify(value) : value 493 }); 494 }; 495 496 /** 497 * Provide a helper method to display a warning. 498 * 499 * @param {String} message 500 * The message to display. 501 * @param {Object} [args] 502 * Arguments to use as replacements in Drupal.formatString. 503 */ 504 Bootstrap.warn = function (message, args) { 505 if (this.settings.dev && console.warn) { 506 console.warn(Drupal.formatString(message, args)); 507 } 508 }; 509 510 /** 511 * Wraps a plugin with common functionality. 512 * 513 * @param {Function} constructor 514 * A plugin constructor being wrapped. 515 * @param {Object|Function} plugin 516 * The plugin being wrapped. 517 * @param {Boolean} [extend = false] 518 * Whether to add super extensibility. 519 */ 520 Bootstrap.wrapPluginConstructor = function (constructor, plugin, extend) { 521 var proto = constructor.prototype; 522 523 // Add a jQuery UI like option getter/setter method. 524 var option = this.option; 525 if (proto.option === void(0)) { 526 proto.option = function () { 527 return option.apply(this, arguments); 528 }; 529 } 530 531 if (extend) { 532 // Handle prototype properties separately. 533 if (plugin.prototype !== void 0) { 534 for (var key in plugin.prototype) { 535 if (!plugin.prototype.hasOwnProperty(key)) continue; 536 var value = plugin.prototype[key]; 537 if (typeof value === 'function') { 538 proto[key] = this.superWrapper(proto[key] || function () {}, value); 539 } 540 else { 541 proto[key] = $.isPlainObject(value) ? $.extend(true, {}, proto[key], value) : value; 542 } 543 } 544 } 545 delete plugin.prototype; 546 547 // Handle static properties. 548 for (key in plugin) { 549 if (!plugin.hasOwnProperty(key)) continue; 550 value = plugin[key]; 551 if (typeof value === 'function') { 552 constructor[key] = this.superWrapper(constructor[key] || function () {}, value); 553 } 554 else { 555 constructor[key] = $.isPlainObject(value) ? $.extend(true, {}, constructor[key], value) : value; 556 } 557 } 558 } 559 }; 560 561 /** 562 * Add Bootstrap to the global Drupal object. 563 * 564 * @type {Bootstrap} 565 */ 566 Drupal.bootstrap = Drupal.bootstrap || Bootstrap; 567 568})(window._, window.jQuery, window.Drupal, window.drupalSettings);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.