PageSourceSearch

https://www.sca-aware.org/themes/bootstrap/js/attributes.js?tliiis

js sca-aware.org collected 2026-09-24 08:54:34 UTC 10,066 bytes, 380 lines download raw bytes

1(function ($, _) {
2
3  /**
4   * @class Attributes
5   *
6   * Modifies attributes.
7   *
8   * @param {Object|Attributes} attributes
9   *   An object to initialize attributes with.
10   */
11  var Attributes = function (attributes) {
12    this.data = {};
13    this.data['class'] = [];
14    this.merge(attributes);
15  };
16
17  /**
18   * Renders the attributes object as a string to inject into an HTML element.
19   *
20   * @return {String}
21   *   A rendered string suitable for inclusion in HTML markup.
22   */
23  Attributes.prototype.toString = function () {
24    var output = '';
25    var name, value;
26    var checkPlain = function (str) {
27      return str && str.toString().replace(/&/g, '&amp;').replace(/"/g, '&quot;').replace(/</g, '&lt;').replace(/>/g, '&gt;') || '';
28    };
29    var data = this.getData();
30    for (name in data) {
31      if (!data.hasOwnProperty(name)) continue;
32      value = data[name];
33      if (_.isFunction(value)) value = value();
34      if (_.isObject(value)) value = _.values(value);
35      if (_.isArray(value)) value = value.join(' ');
36      output += ' ' + checkPlain(name) + '="' + checkPlain(value) + '"';
37    }
38    return output;
39  };
40
41  /**
42   * Renders the Attributes object as a plain object.
43   *
44   * @return {Object}
45   *   A plain object suitable for inclusion in DOM elements.
46   */
47  Attributes.prototype.toPlainObject = function () {
48    var object = {};
49    var name, value;
50    var data = this.getData();
51    for (name in data) {
52      if (!data.hasOwnProperty(name)) continue;
53      value = data[name];
54      if (_.isFunction(value)) value = value();
55      if (_.isObject(value)) value = _.values(value);
56      if (_.isArray(value)) value = value.join(' ');
57      object[name] = value;
58    }
59    return object;
60  };
61
62  /**
63   * Add class(es) to the array.
64   *
65   * @param {string|Array} value
66   *   An individual class or an array of classes to add.
67   *
68   * @return {Attributes}
69   *
70   * @chainable
71   */
72  Attributes.prototype.addClass = function (value) {
73    var args = Array.prototype.slice.call(arguments);
74    this.data['class'] = this.sanitizeClasses(this.data['class'].concat(args));
75    return this;
76  };
77
78  /**
79   * Returns whether the requested attribute exists.
80   *
81   * @param {string} name
82   *   An attribute name to check.
83   *
84   * @return {boolean}
85   *   TRUE or FALSE
86   */
87  Attributes.prototype.exists = function (name) {
88    return this.data[name] !== void(0) && this.data[name] !== null;
89  };
90
91  /**
92   * Retrieve a specific attribute from the array.
93   *
94   * @param {string} name
95   *   The specific attribute to retrieve.
96   * @param {*} defaultValue
97   *   (optional) The default value to set if the attribute does not exist.
98   *
99   * @return {*}
100   *   A specific attribute value, passed by reference.
101   */
102  Attributes.prototype.get = function (name, defaultValue) {
103    if (!this.exists(name)) this.data[name] = defaultValue;
104    return this.data[name];
105  };
106
107  /**
108   * Retrieves a cloned copy of the internal attributes data object.
109   *
110   * @return {Object}
111   */
112  Attributes.prototype.getData = function () {
113    return _.extend({}, this.data);
114  };
115
116  /**
117   * Retrieves classes from the array.
118   *
119   * @return {Array}
120   *   The classes array.
121   */
122  Attributes.prototype.getClasses = function () {
123    return this.get('class', []);
124  };
125
126  /**
127   * Indicates whether a class is present in the array.
128   *
129   * @param {string|Array} className
130   *   The class(es) to search for.
131   *
132   * @return {boolean}
133   *   TRUE or FALSE
134   */
135  Attributes.prototype.hasClass = function (className) {
136    className = this.sanitizeClasses(Array.prototype.slice.call(arguments));
137    var classes = this.getClasses();
138    for (var i = 0, l = className.length; i < l; i++) {
139      // If one of the classes fails, immediately return false.
140      if (_.indexOf(classes, className[i]) === -1) {
141        return false;
142      }
143    }
144    return true;
145  };
146
147  /**
148   * Merges multiple values into the array.
149   *
150   * @param {Attributes|Node|jQuery|Object} object
151   *   An Attributes object with existing data, a Node DOM element, a jQuery
152   *   instance or a plain object where the key is the attribute name and the
153   *   value is the attribute value.
154   * @param {boolean} [recursive]
155   *   Flag determining whether or not to recursively merge key/value pairs.
156   *
157   * @return {Attributes}
158   *
159   * @chainable
160   */
161  Attributes.prototype.merge = function (object, recursive) {
162    // Immediately return if there is nothing to merge.
163    if (!object) {
164      return this;
165    }
166
167    // Get attributes from a jQuery element.
168    if (object instanceof $) {
169      object = object[0];
170    }
171
172    // Get attributes from a DOM element.
173    if (object instanceof Node) {
174      object = Array.prototype.slice.call(object.attributes).reduce(function (attributes, attribute) {
175        attributes[attribute.name] = attribute.value;
176        return attributes;
177      }, {});
178    }
179    // Get attributes from an Attributes instance.
180    else if (object instanceof Attributes) {
181      object = object.getData();
182    }
183    // Otherwise, clone the object.
184    else {
185      object = _.extend({}, object);
186    }
187
188    // By this point, there should be a valid plain object.
189    if (!$.isPlainObject(object)) {
190      setTimeout(function () {
191        throw new Error('Passed object is not supported: ' + object);
192      });
193      return this;
194    }
195
196    // Handle classes separately.
197    if (object && object['class'] !== void 0) {
198      this.addClass(object['class']);
199      delete object['class'];
200    }
201
202    if (recursive === void 0 || recursive) {
203      this.data = $.extend(true, {}, this.data, object);
204    }
205    else {
206      this.data = $.extend({}, this.data, object);
207    }
208
209    return this;
210  };
211
212  /**
213   * Removes an attribute from the array.
214   *
215   * @param {string} name
216   *   The name of the attribute to remove.
217   *
218   * @return {Attributes}
219   *
220   * @chainable
221   */
222  Attributes.prototype.remove = function (name) {
223    if (this.exists(name)) delete this.data[name];
224    return this;
225  };
226
227  /**
228   * Removes a class from the attributes array.
229   *
230   * @param {...string|Array} className
231   *   An individual class or an array of classes to remove.
232   *
233   * @return {Attributes}
234   *
235   * @chainable
236   */
237  Attributes.prototype.removeClass = function (className) {
238    var remove = this.sanitizeClasses(Array.prototype.slice.apply(arguments));
239    this.data['class'] = _.without(this.getClasses(), remove);
240    return this;
241  };
242
243  /**
244   * Replaces a class in the attributes array.
245   *
246   * @param {string} oldValue
247   *   The old class to remove.
248   * @param {string} newValue
249   *   The new class. It will not be added if the old class does not exist.
250   *
251   * @return {Attributes}
252   *
253   * @chainable
254   */
255  Attributes.prototype.replaceClass = function (oldValue, newValue) {
256    var classes = this.getClasses();
257    var i = _.indexOf(this.sanitizeClasses(oldValue), classes);
258    if (i >= 0) {
259      classes[i] = newValue;
260      this.set('class', classes);
261    }
262    return this;
263  };
264
265  /**
266   * Ensures classes are flattened into a single is an array and sanitized.
267   *
268   * @param {...String|Array} classes
269   *   The class or classes to sanitize.
270   *
271   * @return {Array}
272   *   A sanitized array of classes.
273   */
274  Attributes.prototype.sanitizeClasses = function (classes) {
275    return _.chain(Array.prototype.slice.call(arguments))
276      // Flatten in case there's a mix of strings and arrays.
277      .flatten()
278
279      // Split classes that may have been added with a space as a separator.
280      .map(function (string) {
281        return string.split(' ');
282      })
283
284      // Flatten again since it was just split into arrays.
285      .flatten()
286
287      // Filter out empty items.
288      .filter()
289
290      // Clean the class to ensure it's a valid class name.
291      .map(function (value) {
292        return Attributes.cleanClass(value);
293      })
294
295      // Ensure classes are unique.
296      .uniq()
297
298      // Retrieve the final value.
299      .value();
300  };
301
302  /**
303   * Sets an attribute on the array.
304   *
305   * @param {string} name
306   *   The name of the attribute to set.
307   * @param {*} value
308   *   The value of the attribute to set.
309   *
310   * @return {Attributes}
311   *
312   * @chainable
313   */
314  Attributes.prototype.set = function (name, value) {
315    var obj = $.isPlainObject(name) ? name : {};
316    if (typeof name === 'string') {
317      obj[name] = value;
318    }
319    return this.merge(obj);
320  };
321
322  /**
323   * Prepares a string for use as a CSS identifier (element, class, or ID name).
324   *
325   * Note: this is essentially a direct copy from
326   * \Drupal\Component\Utility\Html::cleanCssIdentifier
327   *
328   * @param {string} identifier
329   *   The identifier to clean.
330   * @param {Object} [filter]
331   *   An object of string replacements to use on the identifier.
332   *
333   * @return {string}
334   *   The cleaned identifier.
335   */
336  Attributes.cleanClass = function (identifier, filter) {
337    filter = filter || {
338      ' ': '-',
339      '_': '-',
340      '/': '-',
341      '[': '-',
342      ']': ''
343    };
344
345    identifier = identifier.toLowerCase();
346
347    if (filter['__'] === void 0) {
348      identifier = identifier.replace('__', '#DOUBLE_UNDERSCORE#');
349    }
350
351    identifier = identifier.replace(Object.keys(filter), Object.keys(filter).map(function(key) { return filter[key]; }));
352
353    if (filter['__'] === void 0) {
354      identifier = identifier.replace('#DOUBLE_UNDERSCORE#', '__');
355    }
356
357    identifier = identifier.replace(/[^\u002D\u0030-\u0039\u0041-\u005A\u005F\u0061-\u007A\u00A1-\uFFFF]/g, '');
358    identifier = identifier.replace(['/^[0-9]/', '/^(-[0-9])|^(--)/'], ['_', '__']);
359
360    return identifier;
361  };
362
363  /**
364   * Creates an Attributes instance.
365   *
366   * @param {object|Attributes} [attributes]
367   *   An object to initialize attributes with.
368   *
369   * @return {Attributes}
370   *   An Attributes instance.
371   *
372   * @constructor
373   */
374  Attributes.create = function (attributes) {
375    return new Attributes(attributes);
376  };
377
378  window.Attributes = Attributes;
379
380})(window.jQuery, window._);

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.