1/*! 2 * jQuery onMutate plugin v1.4 3 * http://jquery.com/ 4 * 5 * Copyright 2016 CROmetrics 6 * Released under the MIT license 7 * https://github.com/eclecto/jQuery-onCreate/blob/master/LICENSE 8 * 9 * Date: 2016-08-23T13:12Z 10 */ 11 12/* global define, module, setTimeout */ 13 14(function (factory) { 15 'use strict'; 16 if (typeof define === 'function' && define.amd) { 17 define(['jquery'], factory); 18 } else if (typeof exports !== 'undefined') { 19 module.exports = factory; 20 } else { 21 factory(jQuery); 22 } 23 24}(function ($, MutationObserver) { 25 if (!$) $ = ($.fn.jquery ? $ : jQuery); 26 if (!MutationObserver) MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver || false; 27 var cbid = 0; 28 29 // Uncomment this to easily test the setInterval fallback. 30 // MutationObserver = false; 31 32 // Constants for mutation types. 33 var CREATE = 'create', 34 MODIFY = 'modify', 35 TEXT = 'text'; 36 37 // Observer class. 38 function Observer(mutcallback) { 39 var realObserver; 40 41 // MutationObserver.observe() polyfill 42 this.observe = function (element, init) { 43 if (MutationObserver) { 44 realObserver = realObserver || new MutationObserver(mutcallback); 45 realObserver.observe(element, init); 46 } else { 47 realObserver = setInterval(mutcallback, 50); 48 } 49 }; 50 51 // MutationObserver.disconnect() polyfill 52 this.disconnect = function () { 53 if (realObserver) { 54 if (MutationObserver) { 55 realObserver.disconnect(); 56 } else { 57 clearInterval(realObserver); 58 } 59 } 60 }; 61 } 62 63 // Callback class to wrap a callback and its properties 64 function Callback(callback, conditions, multi) { 65 this.callback = callback; 66 this.conditions = conditions; 67 this.multi = multi || false; 68 this.cbid = cbid++; 69 this.processed = $([]); 70 } 71 72 // Create a map of an element's attributes so we can poll for changes (non-MutationObserver browsers) 73 function attributeMap(element) { 74 var map = {}; 75 76 if (element.attributes) { 77 $.each(element.attributes, function (index, attr) { 78 map[attr.name] = attr.value; 79 }); 80 } 81 82 return map; 83 } 84 85 // Returns a set of MutationRecord-like objects by comparing two attribute maps. 86 function mapCompare(map1, map2) { 87 var mutations = []; 88 $.each(map1, function (key, value) { 89 if (typeof (map2[key]) === 'undefined' || value !== map2[key]) { 90 mutations.push({ 91 attributeName: key 92 }); 93 } 94 }); 95 $.each(map2, function (key) { 96 if (typeof (map1[key]) === 'undefined') { 97 mutations.push({ 98 attributeName: key 99 }); 100 } 101 }); 102 return mutations; 103 } 104 105 // Check attributes 106 function checkAttrs(mutation, $el, callback) { 107 var attrname = mutation.attributeName, 108 attrval = $el.attr(attrname), 109 conditions = callback.conditions || false, 110 attrs = conditions ? conditions[0] : false, 111 match = conditions && conditions[1] ? conditions[1] : false; 112 // If no attrs are set or the mutation affects one of the targeted attributes 113 if (!attrs || attrs.indexOf(attrname) > -1) { 114 if (match) { 115 return (attrval.search(match) >= 0); 116 } else { 117 return true; 118 } 119 } 120 return false; 121 } 122 123 124 // Possible methods to invoke with $().on[Create|Modify|Text]() 125 var methods = { 126 // Attach observers. 127 attach: function (options) { 128 var type = options.type, 129 callback = options.callback, 130 multi = options.multi || false, 131 conditions = options.conditions; 132 133 // Get/initiate the element's onMutate data. 134 if(!this.data('onMutate')) this.data('onMutate', { 135 create: { 136 callbacks: [], 137 ignore: false 138 }, 139 modify: { 140 callbacks: [], 141 ignore: false 142 }, 143 text: { 144 callbacks: [], 145 ignore: false 146 } 147 }); 148 var om = this.data('onMutate')[type], 149 callbacks = om.callbacks, 150 // Add the callback to the array of callbacks for the current type. 151 newcb = new Callback(callback, conditions, multi); 152 callbacks.unshift(newcb); 153 154 // Store the element's current text or attributes if needed. 155 if (!MutationObserver && type === MODIFY) { 156 om.attributeMap = attributeMap(this[0]); 157 } 158 if (type === TEXT) om.text = this.text(); 159 160 var $this = this, 161 i, changematch; 162 163 // Define our callback for when a mutation is detected. 164 var mutcallback = om.mutcallback = om.mutcallback || function (mutations) { 165 callbacks = om.callbacks; // Refresh the callback list. 166 // Ignore any DOM changes that this callback makes to prevent infinite loops. 167 if (!om.ignore) { 168 om.ignore = true; 169 } else { 170 return; 171 } 172 173 var newmap, newtext; 174 if (type === MODIFY) newmap = attributeMap($this[0]); 175 if (type === TEXT) newtext = $this.text(); 176 177 for (i = callbacks.length - 1; i >= 0; i--) { 178 var selected = (type === CREATE ? $(callbacks[i].conditions, $this) : $this); 179 var proc = callbacks[i].processed; 180 var elements = selected.not(proc); 181 182 // Validate changed attributes for modify observers. 183 if (type === MODIFY) { 184 changematch = false;
185 if (!MutationObserver) { 186 mutations = mapCompare(newmap, om.attributeMap); 187 } 188 for (var j = 0; j < mutations.length; j++) { 189 changematch = changematch || checkAttrs(mutations[j], $this, callbacks[i]); 190 } 191 } 192 193 // Compare the text contents of the element to its original text. 194 if (type === TEXT) { 195 changematch = false; 196 var cond = callbacks[i].conditions; 197 if (newtext !== om.text) { 198 if (cond) { 199 changematch = newtext.search(cond) > -1; 200 } else { 201 changematch = true; 202 } 203 } 204 } 205 206 if (elements.length > 0 && (type === CREATE || changematch)) { 207 if (type === CREATE) proc = proc.add(elements); 208 callbacks[i].processed = proc; 209 callbacks[i].callback.call($this, elements); 210 // Remove callback from the list if it only runs once. 211 if (!callbacks[i].multi) { 212 callbacks.splice(i, 1); 213 } 214 } 215 // Update the current values, accounting for any changes the callback(s) might have made. 216 if (type === MODIFY) om.attributeMap = attributeMap($this[0]); 217 if (type === TEXT) om.text = $this.text(); 218 } 219 220 // We've safely iterated through the callbacks, so don't ignore this master Callback anymore. 221 // Additional mutation events apparently fire after this entire function, so we set ignore to false with an extremely small delay. 222 if (om.ignore) setTimeout(function () { 223 om.ignore = false; 224 }, 1); 225 if (callbacks.length === 0) { 226 if (observer) { 227 observer.disconnect(); 228 } else { 229 return true; 230 } 231 } 232 }; 233 // Sanity Check: If this is a create listener, run the callback on initialization to see if there is already a valid element. 234 if (type === CREATE && mutcallback()) { 235 return this; 236 } 237 238 var observer = om.observer = om.observer || new Observer(mutcallback), 239 init; 240 switch (type) { 241 case CREATE: 242 init = { 243 childList: true, 244 subtree: true 245 }; 246 break; 247 case MODIFY: 248 init = { 249 attributes: true 250 }; 251 if (conditions) { 252 init.attributeFilter = conditions[0].split(' '); 253 } 254 break; 255 case TEXT: 256 init = { 257 childList: true, 258 characterData: true, 259 subtree: true 260 }; 261 break; 262 } 263 observer.observe($this[0], init); 264 return this; 265 }, 266 267 // Detach observers. 268 detach: function (options) { 269 if (!this.data('onMutate')) return this; 270 271 var type = options.type, 272 om = this.data('onMutate')[type], 273 callbacks = om.callbacks, 274 callback = options.callback; 275 if (callback) { 276 for (var i = callbacks.length - 1; i >= 0; i--) { 277 if (callbacks[i].callback.prototype.cbid === callback.prototype.cbid) { 278 callbacks.splice(i, i + 1); 279 } 280 } 281 } else { 282 om.callbacks = []; 283 } 284 if (!callbacks.length) { 285 om.observer.disconnect(); 286 } 287 return this; 288 } 289 }; 290 291 $.fn.extend({ 292 // $(parent).onCreate(selector, callback[, multi]); 293 onCreate: function () { 294 295 var args = Array.prototype.slice.call(arguments); 296 297 // Invalid argument 298 if (typeof (args[0]) !== 'string') { 299 return this; 300 } 301 302 if (args[0] === 'detach') { 303 return methods.detach.call(this, { 304 type: CREATE, 305 callback: args[1] 306 }); 307 } else 308 // Default init functionality. 309 if (typeof (args[0]) === 'string' && typeof (args[1]) === 'function') { 310 return methods.attach.call(this, { 311 type: CREATE, 312 conditions: args[0], 313 callback: args[1], 314 multi: args[2] 315 }); 316 } else { 317 return this; 318 } 319 }, 320 321 // onModify. Usage: .onModify([attributes[, match,]] callback, multi) 322 onModify: function () { 323 324 var args = Array.prototype.slice.call(arguments), 325 method; 326 if (methods[args[0]]) { 327 method = args.shift(); 328 } else { 329 method = 'attach'; 330 } 331 332 if (typeof (args[0]) === 'function') { 333 return methods[method].call(this, { 334 type: MODIFY, 335 callback: args[0], 336 multi: args[1] 337 }); 338 } else if (typeof (args[0]) === 'string') { 339 if (typeof (args[1]) === 'function') { 340 return methods[method].call(this, { 341 type: MODIFY, 342 conditions: [args[0]], 343 callback: args[1], 344 multi: args[2] 345 }); 346 } else if (typeof (args[1]) === 'string' || args[1] instanceof RegExp) { 347 return methods[method].call(this, { 348 type: MODIFY, 349 conditions: [args[0], args[1]], 350 callback: args[2], 351 multi: args[3] 352 }); 353 } 354 } else if (method === 'attach') { 355 return this; 356 } else { 357 methods[method].call(this, { 358 type: MODIFY 359 }); 360 } 361 }, 362 363 // onText. Usage: onText([match,] callback, multi); 364 onText: function () { 365 if (this.length === 0) { 366 return this; 367 } 368 369 var args = Array.prototype.slice.call(arguments), 370 method; 371 372 if (methods[args[0]]) { 373 method = args.shift(); 374 } else { 375 method = 'attach'; 376 } 377 378 if (typeof (args[0]) === 'function') { 379 return methods[method].call(this, { 380 type: TEXT, 381 callback: args[0], 382 multi: args[1] 383 }); 384 } else if (typeof (args[0]) === 'string' || args[0] instanceof RegExp && typeof (args[1]) === 'function') { 385 return methods[method].call(this, { 386 type: TEXT, 387 conditions: args[0], 388 callback: args[1], 389 multi: args[2] 390 }); 391 } else if (method === 'attach') { 392 return this; 393 } else { 394 methods[method].call(this, { 395 type: TEXT 396 }); 397 } 398 }, 399 400 // onText. Usage: onMutate(type, ...); 401 onMutate: function () { 402 var args = Array.prototype.slice.call(arguments), 403 type = args.shift(), 404 typeMethod = 'on' + type.charAt(0).toUpperCase() + type.slice(1).toLowerCase();
405 if ($.fn[typeMethod]) $.fn[typeMethod].apply(this, args); 406 } 407 }); 408 // Use $.onCreate as a shortcut to onCreate at the document level. 409 $.onCreate = function (selector, callback, multi) { 410 $(document).onCreate(selector, callback, multi); 411 }; 412}));
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.