1/*global define, window, document*/ 2 3//Polyfill for Object.create. 4if (typeof Object.create !== 'function') { 5 Object.create = (function () { 6 "use strict"; 7 var Temp = function () {}; 8 return function (prototype) { 9 if (arguments.length > 1) { 10 throw new Error('Second argument not supported'); 11 } 12 if (typeof prototype !== 'object') { 13 throw new TypeError('Argument must be an object'); 14 } 15 Temp.prototype = prototype; 16 var result = new Temp(); 17 Temp.prototype = null; 18 return result; 19 }; 20 }()); 21} 22 23(function (root, factory) { 24 "use strict"; 25 if (typeof define === 'function' && define.amd) { 26 define("Behaviors", ["jquery"], factory); 27 } else { 28 root.Behaviors = factory(root.jQuery); 29 } 30}(this, function ($) { 31 "use strict"; 32 33 var module = {}, 34 behavior_registry = {}, 35 content_ready_listeners = [], 36 ElementMissingError = error("ElementMissingError"); 37 38 /* Throttle an event handler. 39 * 40 * Returns a function which, no matter how frequently it's called, will 41 * only trigger a maximum of once per timeout period. More specifically, 42 * the first event will always be processed, then, no events will process 43 * until the end of the timeout period. If one or more events occurred 44 * during this period, the last event recieved will trigger immediately 45 * after the end of the timeout period, as well as restart the throttling 46 * period. Any preceding events will be discarded. 47 * 48 * Not to be confused with a debounce, which only fires the event handler 49 * at the end of a string of events spaced closer than the timeout period. 50 * 51 * The nature of this function means that any passed in function's return 52 * value will be discarded. 53 */ 54 function throttle_single(func, timeout) { 55 var lastTimeout, afterLastArgs, afterLastThis; 56 57 function unthrottle() { 58 if (afterLastArgs !== undefined) { 59 func.apply(afterLastThis, afterLastArgs); 60 afterLastArgs = undefined; 61 lastTimeout = window.setTimeout(unthrottle, timeout); 62 } else { 63 lastTimeout = undefined; 64 } 65 } 66 67 return function () { 68 var myThis = this, myArgs = [], i; 69 70 for (i = 0; i < arguments.length; i += 1) { 71 myArgs.push(arguments[i]); 72 } 73 74 if (lastTimeout === undefined) { 75 func.apply(myThis, myArgs); 76 lastTimeout = window.setTimeout(unthrottle, timeout); 77 } else { 78 afterLastArgs = myArgs; 79 afterLastThis = myThis; 80 } 81 }; 82 } 83 84 function Behavior(elem) { 85 //Do something to elem 86 this.$elem = $(elem); 87 } 88 89 /* Find a behavior's markup. 90 * 91 * The $context argument passed to this function is the jQuery element that 92 * will be searched for behaviors. Any additional arguments will be passed 93 * to Behavior.locate and ultimately to the behavior's constructor. 94 */ 95 Behavior.find_markup = function ($context) { 96 var results = [], i, splitArgs = [], Class = this; 97 98 for (i = 1; i < arguments.length; i += 1) { 99 splitArgs.push(arguments[i]); 100 } 101 102 function processElem(index, elem) { 103 var locateArgs = [elem].concat(splitArgs); 104 105 results.push(Class.locate.apply(Class, locateArgs)); 106 } 107 108 $context.filter(Class.QUERY).each(processElem); 109 $context.find(Class.QUERY).each(processElem); 110 111 return results; 112 }; 113 114 /* Locate a behavior onto an element, returning an instance of that 115 * behavior that you can work with. 116 * 117 * A behavior locates onto an element by instantiating an instance of 118 * itself and installing it onto the markup's jQuery data. Therefore, we 119 * will only instantiate that behavior once; and further calls to .locate 120 * instead return the same object. Thus, it is safe to use .locate as a 121 * general accessor - it is idempotent. 122 * 123 * The elem argument indicates the element that the behavior should locate 124 * onto. Further arguments are passed onto the constructor. 125 * 126 * TODO: Is there a non-jQuery way of handling this? 127 */ 128 Behavior.locate = function (elem) { 129 var $elem = $(elem), new_object, i, objectArgs = [elem], Class = this, 130 rc = $elem.data("behaviors-registered-classes"); 131 132 if ($elem.length === 0) { 133 throw new ElementMissingError("Attempted to locate a Behavior onto an empty element query."); 134 } 135 136 if (rc === undefined) { 137 rc = {}; 138 } 139 140 if (rc[Class.name] === undefined) { 141 //Grab the other arguments 142 for (i = 1; i < arguments.length; i += 1) { 143 objectArgs.push(arguments[i]); 144 } 145 146 new_object = Object.create(Class.prototype); 147 Class.apply(new_object, objectArgs); 148 rc[Class.name] = new_object; 149 } else { 150 new_object = rc[Class.name]; 151 } 152 153 $elem.data("behaviors-registered-classes", rc); 154 155 return new_object; 156 }; 157 158 /* Respond to the presence of new content on the page. 159 * 160 * By default, we attempt to find markup on all children of the context. 161 * Subclasses may do something crazier, like say delay behavior processing 162 * until some third-party API is loaded. 163 * 164 * Consider this roughly equivalent to $(document).ready() callbacks. 165 */ 166 Behavior.content_ready = function ($context) { 167 var Class = this; 168 169 Class.find_markup($context); 170 }; 171 172 /* Respond to the impending removal of content from the page. 173 * 174 * Most behaviors that only attach event handlers to their own content are 175 * safe and do not need to implement content removal support: they will 176 * inherently "fall away". 177 * 178 * However, behaviors that run a constant animation kernel or attach event 179 * handlers to elements outside of their own ownership must provide a 180 * mechanism to detach those event handlers and stop those kernels. 181 */ 182 Behavior.content_removal = function ($context) { 183 var Class = this, 184 $attached_elems = $context.find(Class.QUERY); 185 186 //Iterate through each element and see if our behavior has located upon 187 //them. We don't just call .find_markup/.locate since we don't want to 188 //risk initializing something just to tear it down one cycle later. 189 $attached_elems.each(function (index, attach_elem) { 190 var $elem = $(attach_elem), 191 rc = $elem.data("behaviors-registered-classes"); 192 193 if (rc === undefined) { 194 return; 195 } 196 197 if (rc[Class.name] === undefined) { 198 return; 199 } 200 201 if (rc[Class.name].deinitialize === undefined) { 202 return; 203 } 204 205 rc[Class.name].deinitialize(); 206 }); 207 }; 208 209 /* Register a behavior so that it can respond to global events such as new 210 * content becoming ready. 211 * 212 * It is not always appropriate to register your behavior to re
212cieve load 213 * events. Generally, if this is a behavior you would initialize yourself, 214 * perhaps with special arguments, then you should not register that here. 215 */ 216 function register_behavior(Class, name) { 217 if (name === undefined) { 218 name = Class.name; 219 } 220 221 behavior_registry[name] = Class; 222 } 223 224 /* Register a function that is called when content is ready. 225 * 226 * This function should only be used for things that are not a Behavior. 227 * Proper behaviors should be registered using register_behavior for future 228 * uses. Non-behavior listeners get registered here so that future uses of 229 * behavior registration do not conflict with non-Behavior listeners. 230 */ 231 function register_content_listener(func) { 232 content_ready_listeners.push(func); 233 } 234 235 /* Indicate that some new content is ready. 236 * 237 * The given content will be passed onto all registered behaviors. 238 * 239 * CMS/frameworks with their own ready mechanism will need to ship their 240 * own replacement/wrapper for this function that pushes calls to this 241 * function over to that mechanism; and calls from that mechanism need to 242 * come back here. 243 */ 244 function content_ready($context) { 245 var k, i; 246 247 function do_later(obj, func) { 248 window.setTimeout(func.bind(obj, $context), 0); 249 } 250 251 for (i = 0; i < content_ready_listeners.length; i += 1) { 252 do_later(undefined, content_ready_listeners[i]); 253 } 254 255 for (k in behavior_registry) { 256 if (behavior_registry.hasOwnProperty(k)) { 257 do_later(behavior_registry[k], behavior_registry[k].content_ready); 258 } 259 } 260 } 261 262 /* Indicate that content is about to be removed. 263 * 264 * Registered behaviors with destructors will be called upon to remove any 265 * external event handlers or animation kernels preventing them from being 266 * terminated by the JS runtime. 267 * 268 * TODO: Add content_removal listener functions. 269 */ 270 function content_removal($context) { 271 var k, i; 272 273 function do_later(obj, func) { 274 window.setTimeout(func.bind(obj, $context), 0); 275 } 276 277 for (k in behavior_registry) { 278 if (behavior_registry.hasOwnProperty(k)) { 279 do_later(behavior_registry[k], behavior_registry[k].content_removal); 280 } 281 } 282 } 283 284 function error(error_class_name, ParentClass) { 285 if (error_class_name === undefined) { 286 throw new Error("Please name your error subclass."); 287 } 288 289 if (!(ParentClass instanceof Function)) { 290 ParentClass = Error; 291 } 292 293 var SubError = function (message) { 294 var err = new Error(message); 295 err.name = error_class_name; 296 297 this.name = error_class_name; 298 this.message = err.message; 299 if (err.stack) { 300 this.stack = err.stack; 301 } 302 }; 303 304 SubError.prototype = new ParentClass("u dont c me"); 305 SubError.prototype.constructor = SubError; 306 SubError.prototype.name = error_class_name; 307 308 delete SubError.prototype.stack; 309 310 return SubError; 311 } 312 313 function inherit(ChildClass, ParentClass) { 314 var k; 315 316 //Use the prototyping system to copy methods from parent to child. 317 ChildClass.prototype = Object.create(ParentClass.prototype); 318 ChildClass.prototype.constructor = ChildClass; 319 ChildClass.prototype.parent = ParentClass.prototype; 320 321 //Manually copy class-level methods from parent to child. 322 for (k in ParentClass) { 323 if (ParentClass.hasOwnProperty(k)) { 324 ChildClass[k] = ParentClass[k]; 325 } 326 } 327 } 328 329 function init(ChildClass, object, args) { 330 ChildClass.prototype.parent.constructor.apply(object, args); 331 } 332 333 /* By default, report the initial page load to registered behaviors. 334 */ 335 $(document).ready(function () { 336 content_ready($(document)); 337 }); 338 339 module.ElementMissingError = ElementMissingError; 340 module.throttle_single = throttle_single; 341 module.Behavior = Behavior; 342 module.error = error; 343 module.inherit = inherit; 344 module.init = init; 345 module.register_behavior = register_behavior; 346 module.content_ready = content_ready; 347 module.content_removal = content_removal; 348 module.register_content_listener = register_content_listener; 349 350 return module; 351}));
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.