├── README.md ├── SpecRunner.html ├── lib ├── chai │ └── chai.js ├── jquery │ └── jquery.js ├── mocha │ ├── mocha.css │ └── mocha.js ├── require │ └── require.js └── underscore │ └── underscore.js ├── spec └── bloomFilterSpec.js └── src ├── bitArray.js ├── bloomFilter.js └── hashFunctions.js /README.md: -------------------------------------------------------------------------------- 1 | # LearnBloomFilters 2 | A testing-driven approach to learning how to implement bloom filters in JavaScript. 3 | 4 | ### Why should I use this? 5 | If you want to learn how to make a bloom filter, of course! This repo uses a test running html document, SpecRunner.html, to 6 | double check your work and (hopefully) guide you through the process of creating your own bloom filter in javascript. This 7 | learning style has been unabashedly stolen from [Hack Reactor](http://www.hackreactor.com/), where I am currently a student. 8 | Thanks for being awesome Hack Reactor! 9 | Bloom filters are amazing data structures that can potentially save a system from costly 'contains' lookups. 10 | For more information on them check out [my blog post](http://blog.adamv.io/Learning-bloom-filters-through-TDD) 11 | , where I give an explanation on their structure and outline some instances where a developer may find them useful. 12 | If you find that this not enough information to fully implement a bloom filter, feel free to drop me a line and request some 13 | clarification and I'll do my best to update the post with the new details. 14 | 15 | ### How do I use this repo? 16 | First of all, fork this repo and clone it to your local machine (or just simply clone it directly if you don't want to use git 17 | to keep track of your progress). 18 | Open up SpecRunner.html in a browser and initially you're going to see a lot of red. These are your failing tests that you 19 | should make pass! 20 | ![Failing SpecRunner](http://i.imgur.com/ILar9jA.png) 21 | These tests require you to create a bloom filter object using the well used [pseudo-classical instantiation](http://javascript.info/tutorial/pseudo-classical-pattern) 22 | pattern. If this doesn't appeal to you, feel free to fork my repo and make one with tests assuming a different pattern. 23 | All of the work you need to do is within the src/bloomFilter.js file. The bit array, that you can create using the given 24 | [bitArray.js library](https://github.com/TheAdamizer/bitArray.js), should be the _storage property on your new object and you're 25 | going to need to store the other parameters passed into the constructor as well. 26 | Some clues: 27 | 28 | * 'm' is the traditional designation given to a bloom filter to represent the desired length of the internal boolean array. 29 | 30 | * The array hashingFunctions is available to you as an array of 3 hashing functions, though technically you won't need them 31 | to implement the bloomFilter, just assume that the user of the bloomFilter (the spec runner) will give your filter the 32 | hashing functions they would like you to use. You should use all of them in your implementation. 33 | 34 | The hashing functions should be used by passing in first the desired range (from 0 to n) of indexes you would like to get 35 | back from the function, followed by the value you would like to hash. An example: 36 | 37 | ```javascript 38 | hashingFunctions[0](100, 'some string to hash'); 39 | \\ returns a hash between the values of 0 and 99 40 | ``` 41 | 42 | Keep working to meet the specified tests. If you need more clarification on what the tests are looking for, click on the 43 | specific requirement in SpecRunner.html and it will show you the mocha tests that need to pass. 44 | ![Expanded requirement](http://i.imgur.com/fhd377j.png) 45 | 46 | Keep coding away and you'll start to see some green. Eventually you'll have a SpecRunner.html that looks like this: 47 | ![Passing tests](http://i.imgur.com/SBgl9hR.png) 48 | and then you should have successfully implemented a bloom filter! 49 | -------------------------------------------------------------------------------- /SpecRunner.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Mocha Bloom Filter Tests 5 | 6 | 7 | 8 | 9 | 10 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 |
30 | 31 | 32 | -------------------------------------------------------------------------------- /lib/mocha/mocha.css: -------------------------------------------------------------------------------- 1 | @charset "utf-8"; 2 | 3 | body { 4 | font: 20px/1.5 "Helvetica Neue", Helvetica, Arial, sans-serif; 5 | padding: 60px 50px; 6 | } 7 | 8 | #mocha ul, #mocha li { 9 | margin: 0; 10 | padding: 0; 11 | } 12 | 13 | #mocha ul { 14 | list-style: none; 15 | } 16 | 17 | #mocha h1, #mocha h2 { 18 | margin: 0; 19 | } 20 | 21 | #mocha h1 { 22 | margin-top: 15px; 23 | font-size: 1em; 24 | font-weight: 200; 25 | } 26 | 27 | #mocha h1 a { 28 | text-decoration: none; 29 | color: inherit; 30 | } 31 | 32 | #mocha h1 a:hover { 33 | text-decoration: underline; 34 | } 35 | 36 | #mocha .suite .suite h1 { 37 | margin-top: 0; 38 | font-size: .8em; 39 | } 40 | 41 | .hidden { 42 | display: none; 43 | } 44 | 45 | #mocha h2 { 46 | font-size: 12px; 47 | font-weight: normal; 48 | cursor: pointer; 49 | } 50 | 51 | #mocha .suite { 52 | margin-left: 15px; 53 | } 54 | 55 | #mocha .test { 56 | margin-left: 15px; 57 | overflow: hidden; 58 | } 59 | 60 | #mocha .test.pending:hover h2::after { 61 | content: '(pending)'; 62 | font-family: arial; 63 | } 64 | 65 | #mocha .test.pass.medium .duration { 66 | background: #C09853; 67 | } 68 | 69 | #mocha .test.pass.slow .duration { 70 | background: #B94A48; 71 | } 72 | 73 | #mocha .test.pass::before { 74 | content: '✓'; 75 | font-size: 12px; 76 | display: block; 77 | float: left; 78 | margin-right: 5px; 79 | color: #00d6b2; 80 | } 81 | 82 | #mocha .test.pass .duration { 83 | font-size: 9px; 84 | margin-left: 5px; 85 | padding: 2px 5px; 86 | color: white; 87 | -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.2); 88 | -moz-box-shadow: inset 0 1px 1px rgba(0,0,0,.2); 89 | box-shadow: inset 0 1px 1px rgba(0,0,0,.2); 90 | -webkit-border-radius: 5px; 91 | -moz-border-radius: 5px; 92 | -ms-border-radius: 5px; 93 | -o-border-radius: 5px; 94 | border-radius: 5px; 95 | } 96 | 97 | #mocha .test.pass.fast .duration { 98 | display: none; 99 | } 100 | 101 | #mocha .test.pending { 102 | color: #0b97c4; 103 | } 104 | 105 | #mocha .test.pending::before { 106 | content: '◦'; 107 | color: #0b97c4; 108 | } 109 | 110 | #mocha .test.fail { 111 | color: #c00; 112 | } 113 | 114 | #mocha .test.fail pre { 115 | color: black; 116 | } 117 | 118 | #mocha .test.fail::before { 119 | content: '✖'; 120 | font-size: 12px; 121 | display: block; 122 | float: left; 123 | margin-right: 5px; 124 | color: #c00; 125 | } 126 | 127 | #mocha .test pre.error { 128 | color: #c00; 129 | max-height: 300px; 130 | overflow: auto; 131 | } 132 | 133 | #mocha .test pre { 134 | display: block; 135 | float: left; 136 | clear: left; 137 | font: 12px/1.5 monaco, monospace; 138 | margin: 5px; 139 | padding: 15px; 140 | border: 1px solid #eee; 141 | border-bottom-color: #ddd; 142 | -webkit-border-radius: 3px; 143 | -webkit-box-shadow: 0 1px 3px #eee; 144 | -moz-border-radius: 3px; 145 | -moz-box-shadow: 0 1px 3px #eee; 146 | } 147 | 148 | #mocha .test h2 { 149 | position: relative; 150 | } 151 | 152 | #mocha .test a.replay { 153 | position: absolute; 154 | top: 3px; 155 | right: 0; 156 | text-decoration: none; 157 | vertical-align: middle; 158 | display: block; 159 | width: 15px; 160 | height: 15px; 161 | line-height: 15px; 162 | text-align: center; 163 | background: #eee; 164 | font-size: 15px; 165 | -moz-border-radius: 15px; 166 | border-radius: 15px; 167 | -webkit-transition: opacity 200ms; 168 | -moz-transition: opacity 200ms; 169 | transition: opacity 200ms; 170 | opacity: 0.3; 171 | color: #888; 172 | } 173 | 174 | #mocha .test:hover a.replay { 175 | opacity: 1; 176 | } 177 | 178 | #mocha-report.pass .test.fail { 179 | display: none; 180 | } 181 | 182 | #mocha-report.fail .test.pass { 183 | display: none; 184 | } 185 | 186 | #mocha-error { 187 | color: #c00; 188 | font-size: 1.5 em; 189 | font-weight: 100; 190 | letter-spacing: 1px; 191 | } 192 | 193 | #mocha-stats { 194 | position: fixed; 195 | top: 15px; 196 | right: 10px; 197 | font-size: 12px; 198 | margin: 0; 199 | color: #888; 200 | } 201 | 202 | #mocha-stats .progress { 203 | float: right; 204 | padding-top: 0; 205 | } 206 | 207 | #mocha-stats em { 208 | color: black; 209 | } 210 | 211 | #mocha-stats a { 212 | text-decoration: none; 213 | color: inherit; 214 | } 215 | 216 | #mocha-stats a:hover { 217 | border-bottom: 1px solid #eee; 218 | } 219 | 220 | #mocha-stats li { 221 | display: inline-block; 222 | margin: 0 5px; 223 | list-style: none; 224 | padding-top: 11px; 225 | } 226 | 227 | #mocha-stats canvas { 228 | width: 40px; 229 | height: 40px; 230 | } 231 | 232 | code .comment { color: #ddd } 233 | code .init { color: #2F6FAD } 234 | code .string { color: #5890AD } 235 | code .keyword { color: #8A6343 } 236 | code .number { color: #2F6FAD } 237 | 238 | @media screen and (max-device-width: 480px) { 239 | body { 240 | padding: 60px 0px; 241 | } 242 | 243 | #stats { 244 | position: absolute; 245 | } 246 | } 247 | -------------------------------------------------------------------------------- /lib/require/require.js: -------------------------------------------------------------------------------- 1 | /** vim: et:ts=4:sw=4:sts=4 2 | * @license RequireJS 2.1.8 Copyright (c) 2010-2012, The Dojo Foundation All Rights Reserved. 3 | * Available via the MIT or new BSD license. 4 | * see: http://github.com/jrburke/requirejs for details 5 | */ 6 | //Not using strict: uneven strict support in browsers, #392, and causes 7 | //problems with requirejs.exec()/transpiler plugins that may not be strict. 8 | /*jslint regexp: true, nomen: true, sloppy: true */ 9 | /*global window, navigator, document, importScripts, setTimeout, opera */ 10 | 11 | var requirejs, require, define; 12 | (function (global) { 13 | var req, s, head, baseElement, dataMain, src, 14 | interactiveScript, currentlyAddingScript, mainScript, subPath, 15 | version = '2.1.8', 16 | commentRegExp = /(\/\*([\s\S]*?)\*\/|([^:]|^)\/\/(.*)$)/mg, 17 | cjsRequireRegExp = /[^.]\s*require\s*\(\s*["']([^'"\s]+)["']\s*\)/g, 18 | jsSuffixRegExp = /\.js$/, 19 | currDirRegExp = /^\.\//, 20 | op = Object.prototype, 21 | ostring = op.toString, 22 | hasOwn = op.hasOwnProperty, 23 | ap = Array.prototype, 24 | apsp = ap.splice, 25 | isBrowser = !!(typeof window !== 'undefined' && navigator && window.document), 26 | isWebWorker = !isBrowser && typeof importScripts !== 'undefined', 27 | //PS3 indicates loaded and complete, but need to wait for complete 28 | //specifically. Sequence is 'loading', 'loaded', execution, 29 | // then 'complete'. The UA check is unfortunate, but not sure how 30 | //to feature test w/o causing perf issues. 31 | readyRegExp = isBrowser && navigator.platform === 'PLAYSTATION 3' ? 32 | /^complete$/ : /^(complete|loaded)$/, 33 | defContextName = '_', 34 | //Oh the tragedy, detecting opera. See the usage of isOpera for reason. 35 | isOpera = typeof opera !== 'undefined' && opera.toString() === '[object Opera]', 36 | contexts = {}, 37 | cfg = {}, 38 | globalDefQueue = [], 39 | useInteractive = false; 40 | 41 | function isFunction(it) { 42 | return ostring.call(it) === '[object Function]'; 43 | } 44 | 45 | function isArray(it) { 46 | return ostring.call(it) === '[object Array]'; 47 | } 48 | 49 | /** 50 | * Helper function for iterating over an array. If the func returns 51 | * a true value, it will break out of the loop. 52 | */ 53 | function each(ary, func) { 54 | if (ary) { 55 | var i; 56 | for (i = 0; i < ary.length; i += 1) { 57 | if (ary[i] && func(ary[i], i, ary)) { 58 | break; 59 | } 60 | } 61 | } 62 | } 63 | 64 | /** 65 | * Helper function for iterating over an array backwards. If the func 66 | * returns a true value, it will break out of the loop. 67 | */ 68 | function eachReverse(ary, func) { 69 | if (ary) { 70 | var i; 71 | for (i = ary.length - 1; i > -1; i -= 1) { 72 | if (ary[i] && func(ary[i], i, ary)) { 73 | break; 74 | } 75 | } 76 | } 77 | } 78 | 79 | function hasProp(obj, prop) { 80 | return hasOwn.call(obj, prop); 81 | } 82 | 83 | function getOwn(obj, prop) { 84 | return hasProp(obj, prop) && obj[prop]; 85 | } 86 | 87 | /** 88 | * Cycles over properties in an object and calls a function for each 89 | * property value. If the function returns a truthy value, then the 90 | * iteration is stopped. 91 | */ 92 | function eachProp(obj, func) { 93 | var prop; 94 | for (prop in obj) { 95 | if (hasProp(obj, prop)) { 96 | if (func(obj[prop], prop)) { 97 | break; 98 | } 99 | } 100 | } 101 | } 102 | 103 | /** 104 | * Simple function to mix in properties from source into target, 105 | * but only if target does not already have a property of the same name. 106 | */ 107 | function mixin(target, source, force, deepStringMixin) { 108 | if (source) { 109 | eachProp(source, function (value, prop) { 110 | if (force || !hasProp(target, prop)) { 111 | if (deepStringMixin && typeof value !== 'string') { 112 | if (!target[prop]) { 113 | target[prop] = {}; 114 | } 115 | mixin(target[prop], value, force, deepStringMixin); 116 | } else { 117 | target[prop] = value; 118 | } 119 | } 120 | }); 121 | } 122 | return target; 123 | } 124 | 125 | //Similar to Function.prototype.bind, but the 'this' object is specified 126 | //first, since it is easier to read/figure out what 'this' will be. 127 | function bind(obj, fn) { 128 | return function () { 129 | return fn.apply(obj, arguments); 130 | }; 131 | } 132 | 133 | function scripts() { 134 | return document.getElementsByTagName('script'); 135 | } 136 | 137 | function defaultOnError(err) { 138 | throw err; 139 | } 140 | 141 | //Allow getting a global that expressed in 142 | //dot notation, like 'a.b.c'. 143 | function getGlobal(value) { 144 | if (!value) { 145 | return value; 146 | } 147 | var g = global; 148 | each(value.split('.'), function (part) { 149 | g = g[part]; 150 | }); 151 | return g; 152 | } 153 | 154 | /** 155 | * Constructs an error with a pointer to an URL with more information. 156 | * @param {String} id the error ID that maps to an ID on a web page. 157 | * @param {String} message human readable error. 158 | * @param {Error} [err] the original error, if there is one. 159 | * 160 | * @returns {Error} 161 | */ 162 | function makeError(id, msg, err, requireModules) { 163 | var e = new Error(msg + '\nhttp://requirejs.org/docs/errors.html#' + id); 164 | e.requireType = id; 165 | e.requireModules = requireModules; 166 | if (err) { 167 | e.originalError = err; 168 | } 169 | return e; 170 | } 171 | 172 | if (typeof define !== 'undefined') { 173 | //If a define is already in play via another AMD loader, 174 | //do not overwrite. 175 | return; 176 | } 177 | 178 | if (typeof requirejs !== 'undefined') { 179 | if (isFunction(requirejs)) { 180 | //Do not overwrite and existing requirejs instance. 181 | return; 182 | } 183 | cfg = requirejs; 184 | requirejs = undefined; 185 | } 186 | 187 | //Allow for a require config object 188 | if (typeof require !== 'undefined' && !isFunction(require)) { 189 | //assume it is a config object. 190 | cfg = require; 191 | require = undefined; 192 | } 193 | 194 | function newContext(contextName) { 195 | var inCheckLoaded, Module, context, handlers, 196 | checkLoadedTimeoutId, 197 | config = { 198 | //Defaults. Do not set a default for map 199 | //config to speed up normalize(), which 200 | //will run faster if there is no default. 201 | waitSeconds: 7, 202 | baseUrl: './', 203 | paths: {}, 204 | pkgs: {}, 205 | shim: {}, 206 | config: {} 207 | }, 208 | registry = {}, 209 | //registry of just enabled modules, to speed 210 | //cycle breaking code when lots of modules 211 | //are registered, but not activated. 212 | enabledRegistry = {}, 213 | undefEvents = {}, 214 | defQueue = [], 215 | defined = {}, 216 | urlFetched = {}, 217 | requireCounter = 1, 218 | unnormalizedCounter = 1; 219 | 220 | /** 221 | * Trims the . and .. from an array of path segments. 222 | * It will keep a leading path segment if a .. will become 223 | * the first path segment, to help with module name lookups, 224 | * which act like paths, but can be remapped. But the end result, 225 | * all paths that use this function should look normalized. 226 | * NOTE: this method MODIFIES the input array. 227 | * @param {Array} ary the array of path segments. 228 | */ 229 | function trimDots(ary) { 230 | var i, part; 231 | for (i = 0; ary[i]; i += 1) { 232 | part = ary[i]; 233 | if (part === '.') { 234 | ary.splice(i, 1); 235 | i -= 1; 236 | } else if (part === '..') { 237 | if (i === 1 && (ary[2] === '..' || ary[0] === '..')) { 238 | //End of the line. Keep at least one non-dot 239 | //path segment at the front so it can be mapped 240 | //correctly to disk. Otherwise, there is likely 241 | //no path mapping for a path starting with '..'. 242 | //This can still fail, but catches the most reasonable 243 | //uses of .. 244 | break; 245 | } else if (i > 0) { 246 | ary.splice(i - 1, 2); 247 | i -= 2; 248 | } 249 | } 250 | } 251 | } 252 | 253 | /** 254 | * Given a relative module name, like ./something, normalize it to 255 | * a real name that can be mapped to a path. 256 | * @param {String} name the relative name 257 | * @param {String} baseName a real name that the name arg is relative 258 | * to. 259 | * @param {Boolean} applyMap apply the map config to the value. Should 260 | * only be done if this normalization is for a dependency ID. 261 | * @returns {String} normalized name 262 | */ 263 | function normalize(name, baseName, applyMap) { 264 | var pkgName, pkgConfig, mapValue, nameParts, i, j, nameSegment, 265 | foundMap, foundI, foundStarMap, starI, 266 | baseParts = baseName && baseName.split('/'), 267 | normalizedBaseParts = baseParts, 268 | map = config.map, 269 | starMap = map && map['*']; 270 | 271 | //Adjust any relative paths. 272 | if (name && name.charAt(0) === '.') { 273 | //If have a base name, try to normalize against it, 274 | //otherwise, assume it is a top-level require that will 275 | //be relative to baseUrl in the end. 276 | if (baseName) { 277 | if (getOwn(config.pkgs, baseName)) { 278 | //If the baseName is a package name, then just treat it as one 279 | //name to concat the name with. 280 | normalizedBaseParts = baseParts = [baseName]; 281 | } else { 282 | //Convert baseName to array, and lop off the last part, 283 | //so that . matches that 'directory' and not name of the baseName's 284 | //module. For instance, baseName of 'one/two/three', maps to 285 | //'one/two/three.js', but we want the directory, 'one/two' for 286 | //this normalization. 287 | normalizedBaseParts = baseParts.slice(0, baseParts.length - 1); 288 | } 289 | 290 | name = normalizedBaseParts.concat(name.split('/')); 291 | trimDots(name); 292 | 293 | //Some use of packages may use a . path to reference the 294 | //'main' module name, so normalize for that. 295 | pkgConfig = getOwn(config.pkgs, (pkgName = name[0])); 296 | name = name.join('/'); 297 | if (pkgConfig && name === pkgName + '/' + pkgConfig.main) { 298 | name = pkgName; 299 | } 300 | } else if (name.indexOf('./') === 0) { 301 | // No baseName, so this is ID is resolved relative 302 | // to baseUrl, pull off the leading dot. 303 | name = name.substring(2); 304 | } 305 | } 306 | 307 | //Apply map config if available. 308 | if (applyMap && map && (baseParts || starMap)) { 309 | nameParts = name.split('/'); 310 | 311 | for (i = nameParts.length; i > 0; i -= 1) { 312 | nameSegment = nameParts.slice(0, i).join('/'); 313 | 314 | if (baseParts) { 315 | //Find the longest baseName segment match in the config. 316 | //So, do joins on the biggest to smallest lengths of baseParts. 317 | for (j = baseParts.length; j > 0; j -= 1) { 318 | mapValue = getOwn(map, baseParts.slice(0, j).join('/')); 319 | 320 | //baseName segment has config, find if it has one for 321 | //this name. 322 | if (mapValue) { 323 | mapValue = getOwn(mapValue, nameSegment); 324 | if (mapValue) { 325 | //Match, update name to the new value. 326 | foundMap = mapValue; 327 | foundI = i; 328 | break; 329 | } 330 | } 331 | } 332 | } 333 | 334 | if (foundMap) { 335 | break; 336 | } 337 | 338 | //Check for a star map match, but just hold on to it, 339 | //if there is a shorter segment match later in a matching 340 | //config, then favor over this star map. 341 | if (!foundStarMap && starMap && getOwn(starMap, nameSegment)) { 342 | foundStarMap = getOwn(starMap, nameSegment); 343 | starI = i; 344 | } 345 | } 346 | 347 | if (!foundMap && foundStarMap) { 348 | foundMap = foundStarMap; 349 | foundI = starI; 350 | } 351 | 352 | if (foundMap) { 353 | nameParts.splice(0, foundI, foundMap); 354 | name = nameParts.join('/'); 355 | } 356 | } 357 | 358 | return name; 359 | } 360 | 361 | function removeScript(name) { 362 | if (isBrowser) { 363 | each(scripts(), function (scriptNode) { 364 | if (scriptNode.getAttribute('data-requiremodule') === name && 365 | scriptNode.getAttribute('data-requirecontext') === context.contextName) { 366 | scriptNode.parentNode.removeChild(scriptNode); 367 | return true; 368 | } 369 | }); 370 | } 371 | } 372 | 373 | function hasPathFallback(id) { 374 | var pathConfig = getOwn(config.paths, id); 375 | if (pathConfig && isArray(pathConfig) && pathConfig.length > 1) { 376 | removeScript(id); 377 | //Pop off the first array value, since it failed, and 378 | //retry 379 | pathConfig.shift(); 380 | context.require.undef(id); 381 | context.require([id]); 382 | return true; 383 | } 384 | } 385 | 386 | //Turns a plugin!resource to [plugin, resource] 387 | //with the plugin being undefined if the name 388 | //did not have a plugin prefix. 389 | function splitPrefix(name) { 390 | var prefix, 391 | index = name ? name.indexOf('!') : -1; 392 | if (index > -1) { 393 | prefix = name.substring(0, index); 394 | name = name.substring(index + 1, name.length); 395 | } 396 | return [prefix, name]; 397 | } 398 | 399 | /** 400 | * Creates a module mapping that includes plugin prefix, module 401 | * name, and path. If parentModuleMap is provided it will 402 | * also normalize the name via require.normalize() 403 | * 404 | * @param {String} name the module name 405 | * @param {String} [parentModuleMap] parent module map 406 | * for the module name, used to resolve relative names. 407 | * @param {Boolean} isNormalized: is the ID already normalized. 408 | * This is true if this call is done for a define() module ID. 409 | * @param {Boolean} applyMap: apply the map config to the ID. 410 | * Should only be true if this map is for a dependency. 411 | * 412 | * @returns {Object} 413 | */ 414 | function makeModuleMap(name, parentModuleMap, isNormalized, applyMap) { 415 | var url, pluginModule, suffix, nameParts, 416 | prefix = null, 417 | parentName = parentModuleMap ? parentModuleMap.name : null, 418 | originalName = name, 419 | isDefine = true, 420 | normalizedName = ''; 421 | 422 | //If no name, then it means it is a require call, generate an 423 | //internal name. 424 | if (!name) { 425 | isDefine = false; 426 | name = '_@r' + (requireCounter += 1); 427 | } 428 | 429 | nameParts = splitPrefix(name); 430 | prefix = nameParts[0]; 431 | name = nameParts[1]; 432 | 433 | if (prefix) { 434 | prefix = normalize(prefix, parentName, applyMap); 435 | pluginModule = getOwn(defined, prefix); 436 | } 437 | 438 | //Account for relative paths if there is a base name. 439 | if (name) { 440 | if (prefix) { 441 | if (pluginModule && pluginModule.normalize) { 442 | //Plugin is loaded, use its normalize method. 443 | normalizedName = pluginModule.normalize(name, function (name) { 444 | return normalize(name, parentName, applyMap); 445 | }); 446 | } else { 447 | normalizedName = normalize(name, parentName, applyMap); 448 | } 449 | } else { 450 | //A regular module. 451 | normalizedName = normalize(name, parentName, applyMap); 452 | 453 | //Normalized name may be a plugin ID due to map config 454 | //application in normalize. The map config values must 455 | //already be normalized, so do not need to redo that part. 456 | nameParts = splitPrefix(normalizedName); 457 | prefix = nameParts[0]; 458 | normalizedName = nameParts[1]; 459 | isNormalized = true; 460 | 461 | url = context.nameToUrl(normalizedName); 462 | } 463 | } 464 | 465 | //If the id is a plugin id that cannot be determined if it needs 466 | //normalization, stamp it with a unique ID so two matching relative 467 | //ids that may conflict can be separate. 468 | suffix = prefix && !pluginModule && !isNormalized ? 469 | '_unnormalized' + (unnormalizedCounter += 1) : 470 | ''; 471 | 472 | return { 473 | prefix: prefix, 474 | name: normalizedName, 475 | parentMap: parentModuleMap, 476 | unnormalized: !!suffix, 477 | url: url, 478 | originalName: originalName, 479 | isDefine: isDefine, 480 | id: (prefix ? 481 | prefix + '!' + normalizedName : 482 | normalizedName) + suffix 483 | }; 484 | } 485 | 486 | function getModule(depMap) { 487 | var id = depMap.id, 488 | mod = getOwn(registry, id); 489 | 490 | if (!mod) { 491 | mod = registry[id] = new context.Module(depMap); 492 | } 493 | 494 | return mod; 495 | } 496 | 497 | function on(depMap, name, fn) { 498 | var id = depMap.id, 499 | mod = getOwn(registry, id); 500 | 501 | if (hasProp(defined, id) && 502 | (!mod || mod.defineEmitComplete)) { 503 | if (name === 'defined') { 504 | fn(defined[id]); 505 | } 506 | } else { 507 | mod = getModule(depMap); 508 | if (mod.error && name === 'error') { 509 | fn(mod.error); 510 | } else { 511 | mod.on(name, fn); 512 | } 513 | } 514 | } 515 | 516 | function onError(err, errback) { 517 | var ids = err.requireModules, 518 | notified = false; 519 | 520 | if (errback) { 521 | errback(err); 522 | } else { 523 | each(ids, function (id) { 524 | var mod = getOwn(registry, id); 525 | if (mod) { 526 | //Set error on module, so it skips timeout checks. 527 | mod.error = err; 528 | if (mod.events.error) { 529 | notified = true; 530 | mod.emit('error', err); 531 | } 532 | } 533 | }); 534 | 535 | if (!notified) { 536 | req.onError(err); 537 | } 538 | } 539 | } 540 | 541 | /** 542 | * Internal method to transfer globalQueue items to this context's 543 | * defQueue. 544 | */ 545 | function takeGlobalQueue() { 546 | //Push all the globalDefQueue items into the context's defQueue 547 | if (globalDefQueue.length) { 548 | //Array splice in the values since the context code has a 549 | //local var ref to defQueue, so cannot just reassign the one 550 | //on context. 551 | apsp.apply(defQueue, 552 | [defQueue.length - 1, 0].concat(globalDefQueue)); 553 | globalDefQueue = []; 554 | } 555 | } 556 | 557 | handlers = { 558 | 'require': function (mod) { 559 | if (mod.require) { 560 | return mod.require; 561 | } else { 562 | return (mod.require = context.makeRequire(mod.map)); 563 | } 564 | }, 565 | 'exports': function (mod) { 566 | mod.usingExports = true; 567 | if (mod.map.isDefine) { 568 | if (mod.exports) { 569 | return mod.exports; 570 | } else { 571 | return (mod.exports = defined[mod.map.id] = {}); 572 | } 573 | } 574 | }, 575 | 'module': function (mod) { 576 | if (mod.module) { 577 | return mod.module; 578 | } else { 579 | return (mod.module = { 580 | id: mod.map.id, 581 | uri: mod.map.url, 582 | config: function () { 583 | var c, 584 | pkg = getOwn(config.pkgs, mod.map.id); 585 | // For packages, only support config targeted 586 | // at the main module. 587 | c = pkg ? getOwn(config.config, mod.map.id + '/' + pkg.main) : 588 | getOwn(config.config, mod.map.id); 589 | return c || {}; 590 | }, 591 | exports: defined[mod.map.id] 592 | }); 593 | } 594 | } 595 | }; 596 | 597 | function cleanRegistry(id) { 598 | //Clean up machinery used for waiting modules. 599 | delete registry[id]; 600 | delete enabledRegistry[id]; 601 | } 602 | 603 | function breakCycle(mod, traced, processed) { 604 | var id = mod.map.id; 605 | 606 | if (mod.error) { 607 | mod.emit('error', mod.error); 608 | } else { 609 | traced[id] = true; 610 | each(mod.depMaps, function (depMap, i) { 611 | var depId = depMap.id, 612 | dep = getOwn(registry, depId); 613 | 614 | //Only force things that have not completed 615 | //being defined, so still in the registry, 616 | //and only if it has not been matched up 617 | //in the module already. 618 | if (dep && !mod.depMatched[i] && !processed[depId]) { 619 | if (getOwn(traced, depId)) { 620 | mod.defineDep(i, defined[depId]); 621 | mod.check(); //pass false? 622 | } else { 623 | breakCycle(dep, traced, processed); 624 | } 625 | } 626 | }); 627 | processed[id] = true; 628 | } 629 | } 630 | 631 | function checkLoaded() { 632 | var map, modId, err, usingPathFallback, 633 | waitInterval = config.waitSeconds * 1000, 634 | //It is possible to disable the wait interval by using waitSeconds of 0. 635 | expired = waitInterval && (context.startTime + waitInterval) < new Date().getTime(), 636 | noLoads = [], 637 | reqCalls = [], 638 | stillLoading = false, 639 | needCycleCheck = true; 640 | 641 | //Do not bother if this call was a result of a cycle break. 642 | if (inCheckLoaded) { 643 | return; 644 | } 645 | 646 | inCheckLoaded = true; 647 | 648 | //Figure out the state of all the modules. 649 | eachProp(enabledRegistry, function (mod) { 650 | map = mod.map; 651 | modId = map.id; 652 | 653 | //Skip things that are not enabled or in error state. 654 | if (!mod.enabled) { 655 | return; 656 | } 657 | 658 | if (!map.isDefine) { 659 | reqCalls.push(mod); 660 | } 661 | 662 | if (!mod.error) { 663 | //If the module should be executed, and it has not 664 | //been inited and time is up, remember it. 665 | if (!mod.inited && expired) { 666 | if (hasPathFallback(modId)) { 667 | usingPathFallback = true; 668 | stillLoading = true; 669 | } else { 670 | noLoads.push(modId); 671 | removeScript(modId); 672 | } 673 | } else if (!mod.inited && mod.fetched && map.isDefine) { 674 | stillLoading = true; 675 | if (!map.prefix) { 676 | //No reason to keep looking for unfinished 677 | //loading. If the only stillLoading is a 678 | //plugin resource though, keep going, 679 | //because it may be that a plugin resource 680 | //is waiting on a non-plugin cycle. 681 | return (needCycleCheck = false); 682 | } 683 | } 684 | } 685 | }); 686 | 687 | if (expired && noLoads.length) { 688 | //If wait time expired, throw error of unloaded modules. 689 | err = makeError('timeout', 'Load timeout for modules: ' + noLoads, null, noLoads); 690 | err.contextName = context.contextName; 691 | return onError(err); 692 | } 693 | 694 | //Not expired, check for a cycle. 695 | if (needCycleCheck) { 696 | each(reqCalls, function (mod) { 697 | breakCycle(mod, {}, {}); 698 | }); 699 | } 700 | 701 | //If still waiting on loads, and the waiting load is something 702 | //other than a plugin resource, or there are still outstanding 703 | //scripts, then just try back later. 704 | if ((!expired || usingPathFallback) && stillLoading) { 705 | //Something is still waiting to load. Wait for it, but only 706 | //if a timeout is not already in effect. 707 | if ((isBrowser || isWebWorker) && !checkLoadedTimeoutId) { 708 | checkLoadedTimeoutId = setTimeout(function () { 709 | checkLoadedTimeoutId = 0; 710 | checkLoaded(); 711 | }, 50); 712 | } 713 | } 714 | 715 | inCheckLoaded = false; 716 | } 717 | 718 | Module = function (map) { 719 | this.events = getOwn(undefEvents, map.id) || {}; 720 | this.map = map; 721 | this.shim = getOwn(config.shim, map.id); 722 | this.depExports = []; 723 | this.depMaps = []; 724 | this.depMatched = []; 725 | this.pluginMaps = {}; 726 | this.depCount = 0; 727 | 728 | /* this.exports this.factory 729 | this.depMaps = [], 730 | this.enabled, this.fetched 731 | */ 732 | }; 733 | 734 | Module.prototype = { 735 | init: function (depMaps, factory, errback, options) { 736 | options = options || {}; 737 | 738 | //Do not do more inits if already done. Can happen if there 739 | //are multiple define calls for the same module. That is not 740 | //a normal, common case, but it is also not unexpected. 741 | if (this.inited) { 742 | return; 743 | } 744 | 745 | this.factory = factory; 746 | 747 | if (errback) { 748 | //Register for errors on this module. 749 | this.on('error', errback); 750 | } else if (this.events.error) { 751 | //If no errback already, but there are error listeners 752 | //on this module, set up an errback to pass to the deps. 753 | errback = bind(this, function (err) { 754 | this.emit('error', err); 755 | }); 756 | } 757 | 758 | //Do a copy of the dependency array, so that 759 | //source inputs are not modified. For example 760 | //"shim" deps are passed in here directly, and 761 | //doing a direct modification of the depMaps array 762 | //would affect that config. 763 | this.depMaps = depMaps && depMaps.slice(0); 764 | 765 | this.errback = errback; 766 | 767 | //Indicate this module has be initialized 768 | this.inited = true; 769 | 770 | this.ignore = options.ignore; 771 | 772 | //Could have option to init this module in enabled mode, 773 | //or could have been previously marked as enabled. However, 774 | //the dependencies are not known until init is called. So 775 | //if enabled previously, now trigger dependencies as enabled. 776 | if (options.enabled || this.enabled) { 777 | //Enable this module and dependencies. 778 | //Will call this.check() 779 | this.enable(); 780 | } else { 781 | this.check(); 782 | } 783 | }, 784 | 785 | defineDep: function (i, depExports) { 786 | //Because of cycles, defined callback for a given 787 | //export can be called more than once. 788 | if (!this.depMatched[i]) { 789 | this.depMatched[i] = true; 790 | this.depCount -= 1; 791 | this.depExports[i] = depExports; 792 | } 793 | }, 794 | 795 | fetch: function () { 796 | if (this.fetched) { 797 | return; 798 | } 799 | this.fetched = true; 800 | 801 | context.startTime = (new Date()).getTime(); 802 | 803 | var map = this.map; 804 | 805 | //If the manager is for a plugin managed resource, 806 | //ask the plugin to load it now. 807 | if (this.shim) { 808 | context.makeRequire(this.map, { 809 | enableBuildCallback: true 810 | })(this.shim.deps || [], bind(this, function () { 811 | return map.prefix ? this.callPlugin() : this.load(); 812 | })); 813 | } else { 814 | //Regular dependency. 815 | return map.prefix ? this.callPlugin() : this.load(); 816 | } 817 | }, 818 | 819 | load: function () { 820 | var url = this.map.url; 821 | 822 | //Regular dependency. 823 | if (!urlFetched[url]) { 824 | urlFetched[url] = true; 825 | context.load(this.map.id, url); 826 | } 827 | }, 828 | 829 | /** 830 | * Checks if the module is ready to define itself, and if so, 831 | * define it. 832 | */ 833 | check: function () { 834 | if (!this.enabled || this.enabling) { 835 | return; 836 | } 837 | 838 | var err, cjsModule, 839 | id = this.map.id, 840 | depExports = this.depExports, 841 | exports = this.exports, 842 | factory = this.factory; 843 | 844 | if (!this.inited) { 845 | this.fetch(); 846 | } else if (this.error) { 847 | this.emit('error', this.error); 848 | } else if (!this.defining) { 849 | //The factory could trigger another require call 850 | //that would result in checking this module to 851 | //define itself again. If already in the process 852 | //of doing that, skip this work. 853 | this.defining = true; 854 | 855 | if (this.depCount < 1 && !this.defined) { 856 | if (isFunction(factory)) { 857 | //If there is an error listener, favor passing 858 | //to that instead of throwing an error. However, 859 | //only do it for define()'d modules. require 860 | //errbacks should not be called for failures in 861 | //their callbacks (#699). However if a global 862 | //onError is set, use that. 863 | if ((this.events.error && this.map.isDefine) || 864 | req.onError !== defaultOnError) { 865 | try { 866 | exports = context.execCb(id, factory, depExports, exports); 867 | } catch (e) { 868 | err = e; 869 | } 870 | } else { 871 | exports = context.execCb(id, factory, depExports, exports); 872 | } 873 | 874 | if (this.map.isDefine) { 875 | //If setting exports via 'module' is in play, 876 | //favor that over return value and exports. After that, 877 | //favor a non-undefined return value over exports use. 878 | cjsModule = this.module; 879 | if (cjsModule && 880 | cjsModule.exports !== undefined && 881 | //Make sure it is not already the exports value 882 | cjsModule.exports !== this.exports) { 883 | exports = cjsModule.exports; 884 | } else if (exports === undefined && this.usingExports) { 885 | //exports already set the defined value. 886 | exports = this.exports; 887 | } 888 | } 889 | 890 | if (err) { 891 | err.requireMap = this.map; 892 | err.requireModules = this.map.isDefine ? [this.map.id] : null; 893 | err.requireType = this.map.isDefine ? 'define' : 'require'; 894 | return onError((this.error = err)); 895 | } 896 | 897 | } else { 898 | //Just a literal value 899 | exports = factory; 900 | } 901 | 902 | this.exports = exports; 903 | 904 | if (this.map.isDefine && !this.ignore) { 905 | defined[id] = exports; 906 | 907 | if (req.onResourceLoad) { 908 | req.onResourceLoad(context, this.map, this.depMaps); 909 | } 910 | } 911 | 912 | //Clean up 913 | cleanRegistry(id); 914 | 915 | this.defined = true; 916 | } 917 | 918 | //Finished the define stage. Allow calling check again 919 | //to allow define notifications below in the case of a 920 | //cycle. 921 | this.defining = false; 922 | 923 | if (this.defined && !this.defineEmitted) { 924 | this.defineEmitted = true; 925 | this.emit('defined', this.exports); 926 | this.defineEmitComplete = true; 927 | } 928 | 929 | } 930 | }, 931 | 932 | callPlugin: function () { 933 | var map = this.map, 934 | id = map.id, 935 | //Map already normalized the prefix. 936 | pluginMap = makeModuleMap(map.prefix); 937 | 938 | //Mark this as a dependency for this plugin, so it 939 | //can be traced for cycles. 940 | this.depMaps.push(pluginMap); 941 | 942 | on(pluginMap, 'defined', bind(this, function (plugin) { 943 | var load, normalizedMap, normalizedMod, 944 | name = this.map.name, 945 | parentName = this.map.parentMap ? this.map.parentMap.name : null, 946 | localRequire = context.makeRequire(map.parentMap, { 947 | enableBuildCallback: true 948 | }); 949 | 950 | //If current map is not normalized, wait for that 951 | //normalized name to load instead of continuing. 952 | if (this.map.unnormalized) { 953 | //Normalize the ID if the plugin allows it. 954 | if (plugin.normalize) { 955 | name = plugin.normalize(name, function (name) { 956 | return normalize(name, parentName, true); 957 | }) || ''; 958 | } 959 | 960 | //prefix and name should already be normalized, no need 961 | //for applying map config again either. 962 | normalizedMap = makeModuleMap(map.prefix + '!' + name, 963 | this.map.parentMap); 964 | on(normalizedMap, 965 | 'defined', bind(this, function (value) { 966 | this.init([], function () { return value; }, null, { 967 | enabled: true, 968 | ignore: true 969 | }); 970 | })); 971 | 972 | normalizedMod = getOwn(registry, normalizedMap.id); 973 | if (normalizedMod) { 974 | //Mark this as a dependency for this plugin, so it 975 | //can be traced for cycles. 976 | this.depMaps.push(normalizedMap); 977 | 978 | if (this.events.error) { 979 | normalizedMod.on('error', bind(this, function (err) { 980 | this.emit('error', err); 981 | })); 982 | } 983 | normalizedMod.enable(); 984 | } 985 | 986 | return; 987 | } 988 | 989 | load = bind(this, function (value) { 990 | this.init([], function () { return value; }, null, { 991 | enabled: true 992 | }); 993 | }); 994 | 995 | load.error = bind(this, function (err) { 996 | this.inited = true; 997 | this.error = err; 998 | err.requireModules = [id]; 999 | 1000 | //Remove temp unnormalized modules for this module, 1001 | //since they will never be resolved otherwise now. 1002 | eachProp(registry, function (mod) { 1003 | if (mod.map.id.indexOf(id + '_unnormalized') === 0) { 1004 | cleanRegistry(mod.map.id); 1005 | } 1006 | }); 1007 | 1008 | onError(err); 1009 | }); 1010 | 1011 | //Allow plugins to load other code without having to know the 1012 | //context or how to 'complete' the load. 1013 | load.fromText = bind(this, function (text, textAlt) { 1014 | /*jslint evil: true */ 1015 | var moduleName = map.name, 1016 | moduleMap = makeModuleMap(moduleName), 1017 | hasInteractive = useInteractive; 1018 | 1019 | //As of 2.1.0, support just passing the text, to reinforce 1020 | //fromText only being called once per resource. Still 1021 | //support old style of passing moduleName but discard 1022 | //that moduleName in favor of the internal ref. 1023 | if (textAlt) { 1024 | text = textAlt; 1025 | } 1026 | 1027 | //Turn off interactive script matching for IE for any define 1028 | //calls in the text, then turn it back on at the end. 1029 | if (hasInteractive) { 1030 | useInteractive = false; 1031 | } 1032 | 1033 | //Prime the system by creating a module instance for 1034 | //it. 1035 | getModule(moduleMap); 1036 | 1037 | //Transfer any config to this other module. 1038 | if (hasProp(config.config, id)) { 1039 | config.config[moduleName] = config.config[id]; 1040 | } 1041 | 1042 | try { 1043 | req.exec(text); 1044 | } catch (e) { 1045 | return onError(makeError('fromtexteval', 1046 | 'fromText eval for ' + id + 1047 | ' failed: ' + e, 1048 | e, 1049 | [id])); 1050 | } 1051 | 1052 | if (hasInteractive) { 1053 | useInteractive = true; 1054 | } 1055 | 1056 | //Mark this as a dependency for the plugin 1057 | //resource 1058 | this.depMaps.push(moduleMap); 1059 | 1060 | //Support anonymous modules. 1061 | context.completeLoad(moduleName); 1062 | 1063 | //Bind the value of that module to the value for this 1064 | //resource ID. 1065 | localRequire([moduleName], load); 1066 | }); 1067 | 1068 | //Use parentName here since the plugin's name is not reliable, 1069 | //could be some weird string with no path that actually wants to 1070 | //reference the parentName's path. 1071 | plugin.load(map.name, localRequire, load, config); 1072 | })); 1073 | 1074 | context.enable(pluginMap, this); 1075 | this.pluginMaps[pluginMap.id] = pluginMap; 1076 | }, 1077 | 1078 | enable: function () { 1079 | enabledRegistry[this.map.id] = this; 1080 | this.enabled = true; 1081 | 1082 | //Set flag mentioning that the module is enabling, 1083 | //so that immediate calls to the defined callbacks 1084 | //for dependencies do not trigger inadvertent load 1085 | //with the depCount still being zero. 1086 | this.enabling = true; 1087 | 1088 | //Enable each dependency 1089 | each(this.depMaps, bind(this, function (depMap, i) { 1090 | var id, mod, handler; 1091 | 1092 | if (typeof depMap === 'string') { 1093 | //Dependency needs to be converted to a depMap 1094 | //and wired up to this module. 1095 | depMap = makeModuleMap(depMap, 1096 | (this.map.isDefine ? this.map : this.map.parentMap), 1097 | false, 1098 | !this.skipMap); 1099 | this.depMaps[i] = depMap; 1100 | 1101 | handler = getOwn(handlers, depMap.id); 1102 | 1103 | if (handler) { 1104 | this.depExports[i] = handler(this); 1105 | return; 1106 | } 1107 | 1108 | this.depCount += 1; 1109 | 1110 | on(depMap, 'defined', bind(this, function (depExports) { 1111 | this.defineDep(i, depExports); 1112 | this.check(); 1113 | })); 1114 | 1115 | if (this.errback) { 1116 | on(depMap, 'error', bind(this, this.errback)); 1117 | } 1118 | } 1119 | 1120 | id = depMap.id; 1121 | mod = registry[id]; 1122 | 1123 | //Skip special modules like 'require', 'exports', 'module' 1124 | //Also, don't call enable if it is already enabled, 1125 | //important in circular dependency cases. 1126 | if (!hasProp(handlers, id) && mod && !mod.enabled) { 1127 | context.enable(depMap, this); 1128 | } 1129 | })); 1130 | 1131 | //Enable each plugin that is used in 1132 | //a dependency 1133 | eachProp(this.pluginMaps, bind(this, function (pluginMap) { 1134 | var mod = getOwn(registry, pluginMap.id); 1135 | if (mod && !mod.enabled) { 1136 | context.enable(pluginMap, this); 1137 | } 1138 | })); 1139 | 1140 | this.enabling = false; 1141 | 1142 | this.check(); 1143 | }, 1144 | 1145 | on: function (name, cb) { 1146 | var cbs = this.events[name]; 1147 | if (!cbs) { 1148 | cbs = this.events[name] = []; 1149 | } 1150 | cbs.push(cb); 1151 | }, 1152 | 1153 | emit: function (name, evt) { 1154 | each(this.events[name], function (cb) { 1155 | cb(evt); 1156 | }); 1157 | if (name === 'error') { 1158 | //Now that the error handler was triggered, remove 1159 | //the listeners, since this broken Module instance 1160 | //can stay around for a while in the registry. 1161 | delete this.events[name]; 1162 | } 1163 | } 1164 | }; 1165 | 1166 | function callGetModule(args) { 1167 | //Skip modules already defined. 1168 | if (!hasProp(defined, args[0])) { 1169 | getModule(makeModuleMap(args[0], null, true)).init(args[1], args[2]); 1170 | } 1171 | } 1172 | 1173 | function removeListener(node, func, name, ieName) { 1174 | //Favor detachEvent because of IE9 1175 | //issue, see attachEvent/addEventListener comment elsewhere 1176 | //in this file. 1177 | if (node.detachEvent && !isOpera) { 1178 | //Probably IE. If not it will throw an error, which will be 1179 | //useful to know. 1180 | if (ieName) { 1181 | node.detachEvent(ieName, func); 1182 | } 1183 | } else { 1184 | node.removeEventListener(name, func, false); 1185 | } 1186 | } 1187 | 1188 | /** 1189 | * Given an event from a script node, get the requirejs info from it, 1190 | * and then removes the event listeners on the node. 1191 | * @param {Event} evt 1192 | * @returns {Object} 1193 | */ 1194 | function getScriptData(evt) { 1195 | //Using currentTarget instead of target for Firefox 2.0's sake. Not 1196 | //all old browsers will be supported, but this one was easy enough 1197 | //to support and still makes sense. 1198 | var node = evt.currentTarget || evt.srcElement; 1199 | 1200 | //Remove the listeners once here. 1201 | removeListener(node, context.onScriptLoad, 'load', 'onreadystatechange'); 1202 | removeListener(node, context.onScriptError, 'error'); 1203 | 1204 | return { 1205 | node: node, 1206 | id: node && node.getAttribute('data-requiremodule') 1207 | }; 1208 | } 1209 | 1210 | function intakeDefines() { 1211 | var args; 1212 | 1213 | //Any defined modules in the global queue, intake them now. 1214 | takeGlobalQueue(); 1215 | 1216 | //Make sure any remaining defQueue items get properly processed. 1217 | while (defQueue.length) { 1218 | args = defQueue.shift(); 1219 | if (args[0] === null) { 1220 | return onError(makeError('mismatch', 'Mismatched anonymous define() module: ' + args[args.length - 1])); 1221 | } else { 1222 | //args are id, deps, factory. Should be normalized by the 1223 | //define() function. 1224 | callGetModule(args); 1225 | } 1226 | } 1227 | } 1228 | 1229 | context = { 1230 | config: config, 1231 | contextName: contextName, 1232 | registry: registry, 1233 | defined: defined, 1234 | urlFetched: urlFetched, 1235 | defQueue: defQueue, 1236 | Module: Module, 1237 | makeModuleMap: makeModuleMap, 1238 | nextTick: req.nextTick, 1239 | onError: onError, 1240 | 1241 | /** 1242 | * Set a configuration for the context. 1243 | * @param {Object} cfg config object to integrate. 1244 | */ 1245 | configure: function (cfg) { 1246 | //Make sure the baseUrl ends in a slash. 1247 | if (cfg.baseUrl) { 1248 | if (cfg.baseUrl.charAt(cfg.baseUrl.length - 1) !== '/') { 1249 | cfg.baseUrl += '/'; 1250 | } 1251 | } 1252 | 1253 | //Save off the paths and packages since they require special processing, 1254 | //they are additive. 1255 | var pkgs = config.pkgs, 1256 | shim = config.shim, 1257 | objs = { 1258 | paths: true, 1259 | config: true, 1260 | map: true 1261 | }; 1262 | 1263 | eachProp(cfg, function (value, prop) { 1264 | if (objs[prop]) { 1265 | if (prop === 'map') { 1266 | if (!config.map) { 1267 | config.map = {}; 1268 | } 1269 | mixin(config[prop], value, true, true); 1270 | } else { 1271 | mixin(config[prop], value, true); 1272 | } 1273 | } else { 1274 | config[prop] = value; 1275 | } 1276 | }); 1277 | 1278 | //Merge shim 1279 | if (cfg.shim) { 1280 | eachProp(cfg.shim, function (value, id) { 1281 | //Normalize the structure 1282 | if (isArray(value)) { 1283 | value = { 1284 | deps: value 1285 | }; 1286 | } 1287 | if ((value.exports || value.init) && !value.exportsFn) { 1288 | value.exportsFn = context.makeShimExports(value); 1289 | } 1290 | shim[id] = value; 1291 | }); 1292 | config.shim = shim; 1293 | } 1294 | 1295 | //Adjust packages if necessary. 1296 | if (cfg.packages) { 1297 | each(cfg.packages, function (pkgObj) { 1298 | var location; 1299 | 1300 | pkgObj = typeof pkgObj === 'string' ? { name: pkgObj } : pkgObj; 1301 | location = pkgObj.location; 1302 | 1303 | //Create a brand new object on pkgs, since currentPackages can 1304 | //be passed in again, and config.pkgs is the internal transformed 1305 | //state for all package configs. 1306 | pkgs[pkgObj.name] = { 1307 | name: pkgObj.name, 1308 | location: location || pkgObj.name, 1309 | //Remove leading dot in main, so main paths are normalized, 1310 | //and remove any trailing .js, since different package 1311 | //envs have different conventions: some use a module name, 1312 | //some use a file name. 1313 | main: (pkgObj.main || 'main') 1314 | .replace(currDirRegExp, '') 1315 | .replace(jsSuffixRegExp, '') 1316 | }; 1317 | }); 1318 | 1319 | //Done with modifications, assing packages back to context config 1320 | config.pkgs = pkgs; 1321 | } 1322 | 1323 | //If there are any "waiting to execute" modules in the registry, 1324 | //update the maps for them, since their info, like URLs to load, 1325 | //may have changed. 1326 | eachProp(registry, function (mod, id) { 1327 | //If module already has init called, since it is too 1328 | //late to modify them, and ignore unnormalized ones 1329 | //since they are transient. 1330 | if (!mod.inited && !mod.map.unnormalized) { 1331 | mod.map = makeModuleMap(id); 1332 | } 1333 | }); 1334 | 1335 | //If a deps array or a config callback is specified, then call 1336 | //require with those args. This is useful when require is defined as a 1337 | //config object before require.js is loaded. 1338 | if (cfg.deps || cfg.callback) { 1339 | context.require(cfg.deps || [], cfg.callback); 1340 | } 1341 | }, 1342 | 1343 | makeShimExports: function (value) { 1344 | function fn() { 1345 | var ret; 1346 | if (value.init) { 1347 | ret = value.init.apply(global, arguments); 1348 | } 1349 | return ret || (value.exports && getGlobal(value.exports)); 1350 | } 1351 | return fn; 1352 | }, 1353 | 1354 | makeRequire: function (relMap, options) { 1355 | options = options || {}; 1356 | 1357 | function localRequire(deps, callback, errback) { 1358 | var id, map, requireMod; 1359 | 1360 | if (options.enableBuildCallback && callback && isFunction(callback)) { 1361 | callback.__requireJsBuild = true; 1362 | } 1363 | 1364 | if (typeof deps === 'string') { 1365 | if (isFunction(callback)) { 1366 | //Invalid call 1367 | return onError(makeError('requireargs', 'Invalid require call'), errback); 1368 | } 1369 | 1370 | //If require|exports|module are requested, get the 1371 | //value for them from the special handlers. Caveat: 1372 | //this only works while module is being defined. 1373 | if (relMap && hasProp(handlers, deps)) { 1374 | return handlers[deps](registry[relMap.id]); 1375 | } 1376 | 1377 | //Synchronous access to one module. If require.get is 1378 | //available (as in the Node adapter), prefer that. 1379 | if (req.get) { 1380 | return req.get(context, deps, relMap, localRequire); 1381 | } 1382 | 1383 | //Normalize module name, if it contains . or .. 1384 | map = makeModuleMap(deps, relMap, false, true); 1385 | id = map.id; 1386 | 1387 | if (!hasProp(defined, id)) { 1388 | return onError(makeError('notloaded', 'Module name "' + 1389 | id + 1390 | '" has not been loaded yet for context: ' + 1391 | contextName + 1392 | (relMap ? '' : '. Use require([])'))); 1393 | } 1394 | return defined[id]; 1395 | } 1396 | 1397 | //Grab defines waiting in the global queue. 1398 | intakeDefines(); 1399 | 1400 | //Mark all the dependencies as needing to be loaded. 1401 | context.nextTick(function () { 1402 | //Some defines could have been added since the 1403 | //require call, collect them. 1404 | intakeDefines(); 1405 | 1406 | requireMod = getModule(makeModuleMap(null, relMap)); 1407 | 1408 | //Store if map config should be applied to this require 1409 | //call for dependencies. 1410 | requireMod.skipMap = options.skipMap; 1411 | 1412 | requireMod.init(deps, callback, errback, { 1413 | enabled: true 1414 | }); 1415 | 1416 | checkLoaded(); 1417 | }); 1418 | 1419 | return localRequire; 1420 | } 1421 | 1422 | mixin(localRequire, { 1423 | isBrowser: isBrowser, 1424 | 1425 | /** 1426 | * Converts a module name + .extension into an URL path. 1427 | * *Requires* the use of a module name. It does not support using 1428 | * plain URLs like nameToUrl. 1429 | */ 1430 | toUrl: function (moduleNamePlusExt) { 1431 | var ext, 1432 | index = moduleNamePlusExt.lastIndexOf('.'), 1433 | segment = moduleNamePlusExt.split('/')[0], 1434 | isRelative = segment === '.' || segment === '..'; 1435 | 1436 | //Have a file extension alias, and it is not the 1437 | //dots from a relative path. 1438 | if (index !== -1 && (!isRelative || index > 1)) { 1439 | ext = moduleNamePlusExt.substring(index, moduleNamePlusExt.length); 1440 | moduleNamePlusExt = moduleNamePlusExt.substring(0, index); 1441 | } 1442 | 1443 | return context.nameToUrl(normalize(moduleNamePlusExt, 1444 | relMap && relMap.id, true), ext, true); 1445 | }, 1446 | 1447 | defined: function (id) { 1448 | return hasProp(defined, makeModuleMap(id, relMap, false, true).id); 1449 | }, 1450 | 1451 | specified: function (id) { 1452 | id = makeModuleMap(id, relMap, false, true).id; 1453 | return hasProp(defined, id) || hasProp(registry, id); 1454 | } 1455 | }); 1456 | 1457 | //Only allow undef on top level require calls 1458 | if (!relMap) { 1459 | localRequire.undef = function (id) { 1460 | //Bind any waiting define() calls to this context, 1461 | //fix for #408 1462 | takeGlobalQueue(); 1463 | 1464 | var map = makeModuleMap(id, relMap, true), 1465 | mod = getOwn(registry, id); 1466 | 1467 | delete defined[id]; 1468 | delete urlFetched[map.url]; 1469 | delete undefEvents[id]; 1470 | 1471 | if (mod) { 1472 | //Hold on to listeners in case the 1473 | //module will be attempted to be reloaded 1474 | //using a different config. 1475 | if (mod.events.defined) { 1476 | undefEvents[id] = mod.events; 1477 | } 1478 | 1479 | cleanRegistry(id); 1480 | } 1481 | }; 1482 | } 1483 | 1484 | return localRequire; 1485 | }, 1486 | 1487 | /** 1488 | * Called to enable a module if it is still in the registry 1489 | * awaiting enablement. A second arg, parent, the parent module, 1490 | * is passed in for context, when this method is overriden by 1491 | * the optimizer. Not shown here to keep code compact. 1492 | */ 1493 | enable: function (depMap) { 1494 | var mod = getOwn(registry, depMap.id); 1495 | if (mod) { 1496 | getModule(depMap).enable(); 1497 | } 1498 | }, 1499 | 1500 | /** 1501 | * Internal method used by environment adapters to complete a load event. 1502 | * A load event could be a script load or just a load pass from a synchronous 1503 | * load call. 1504 | * @param {String} moduleName the name of the module to potentially complete. 1505 | */ 1506 | completeLoad: function (moduleName) { 1507 | var found, args, mod, 1508 | shim = getOwn(config.shim, moduleName) || {}, 1509 | shExports = shim.exports; 1510 | 1511 | takeGlobalQueue(); 1512 | 1513 | while (defQueue.length) { 1514 | args = defQueue.shift(); 1515 | if (args[0] === null) { 1516 | args[0] = moduleName; 1517 | //If already found an anonymous module and bound it 1518 | //to this name, then this is some other anon module 1519 | //waiting for its completeLoad to fire. 1520 | if (found) { 1521 | break; 1522 | } 1523 | found = true; 1524 | } else if (args[0] === moduleName) { 1525 | //Found matching define call for this script! 1526 | found = true; 1527 | } 1528 | 1529 | callGetModule(args); 1530 | } 1531 | 1532 | //Do this after the cycle of callGetModule in case the result 1533 | //of those calls/init calls changes the registry. 1534 | mod = getOwn(registry, moduleName); 1535 | 1536 | if (!found && !hasProp(defined, moduleName) && mod && !mod.inited) { 1537 | if (config.enforceDefine && (!shExports || !getGlobal(shExports))) { 1538 | if (hasPathFallback(moduleName)) { 1539 | return; 1540 | } else { 1541 | return onError(makeError('nodefine', 1542 | 'No define call for ' + moduleName, 1543 | null, 1544 | [moduleName])); 1545 | } 1546 | } else { 1547 | //A script that does not call define(), so just simulate 1548 | //the call for it. 1549 | callGetModule([moduleName, (shim.deps || []), shim.exportsFn]); 1550 | } 1551 | } 1552 | 1553 | checkLoaded(); 1554 | }, 1555 | 1556 | /** 1557 | * Converts a module name to a file path. Supports cases where 1558 | * moduleName may actually be just an URL. 1559 | * Note that it **does not** call normalize on the moduleName, 1560 | * it is assumed to have already been normalized. This is an 1561 | * internal API, not a public one. Use toUrl for the public API. 1562 | */ 1563 | nameToUrl: function (moduleName, ext, skipExt) { 1564 | var paths, pkgs, pkg, pkgPath, syms, i, parentModule, url, 1565 | parentPath; 1566 | 1567 | //If a colon is in the URL, it indicates a protocol is used and it is just 1568 | //an URL to a file, or if it starts with a slash, contains a query arg (i.e. ?) 1569 | //or ends with .js, then assume the user meant to use an url and not a module id. 1570 | //The slash is important for protocol-less URLs as well as full paths. 1571 | if (req.jsExtRegExp.test(moduleName)) { 1572 | //Just a plain path, not module name lookup, so just return it. 1573 | //Add extension if it is included. This is a bit wonky, only non-.js things pass 1574 | //an extension, this method probably needs to be reworked. 1575 | url = moduleName + (ext || ''); 1576 | } else { 1577 | //A module that needs to be converted to a path. 1578 | paths = config.paths; 1579 | pkgs = config.pkgs; 1580 | 1581 | syms = moduleName.split('/'); 1582 | //For each module name segment, see if there is a path 1583 | //registered for it. Start with most specific name 1584 | //and work up from it. 1585 | for (i = syms.length; i > 0; i -= 1) { 1586 | parentModule = syms.slice(0, i).join('/'); 1587 | pkg = getOwn(pkgs, parentModule); 1588 | parentPath = getOwn(paths, parentModule); 1589 | if (parentPath) { 1590 | //If an array, it means there are a few choices, 1591 | //Choose the one that is desired 1592 | if (isArray(parentPath)) { 1593 | parentPath = parentPath[0]; 1594 | } 1595 | syms.splice(0, i, parentPath); 1596 | break; 1597 | } else if (pkg) { 1598 | //If module name is just the package name, then looking 1599 | //for the main module. 1600 | if (moduleName === pkg.name) { 1601 | pkgPath = pkg.location + '/' + pkg.main; 1602 | } else { 1603 | pkgPath = pkg.location; 1604 | } 1605 | syms.splice(0, i, pkgPath); 1606 | break; 1607 | } 1608 | } 1609 | 1610 | //Join the path parts together, then figure out if baseUrl is needed. 1611 | url = syms.join('/'); 1612 | url += (ext || (/\?/.test(url) || skipExt ? '' : '.js')); 1613 | url = (url.charAt(0) === '/' || url.match(/^[\w\+\.\-]+:/) ? '' : config.baseUrl) + url; 1614 | } 1615 | 1616 | return config.urlArgs ? url + 1617 | ((url.indexOf('?') === -1 ? '?' : '&') + 1618 | config.urlArgs) : url; 1619 | }, 1620 | 1621 | //Delegates to req.load. Broken out as a separate function to 1622 | //allow overriding in the optimizer. 1623 | load: function (id, url) { 1624 | req.load(context, id, url); 1625 | }, 1626 | 1627 | /** 1628 | * Executes a module callback function. Broken out as a separate function 1629 | * solely to allow the build system to sequence the files in the built 1630 | * layer in the right sequence. 1631 | * 1632 | * @private 1633 | */ 1634 | execCb: function (name, callback, args, exports) { 1635 | return callback.apply(exports, args); 1636 | }, 1637 | 1638 | /** 1639 | * callback for script loads, used to check status of loading. 1640 | * 1641 | * @param {Event} evt the event from the browser for the script 1642 | * that was loaded. 1643 | */ 1644 | onScriptLoad: function (evt) { 1645 | //Using currentTarget instead of target for Firefox 2.0's sake. Not 1646 | //all old browsers will be supported, but this one was easy enough 1647 | //to support and still makes sense. 1648 | if (evt.type === 'load' || 1649 | (readyRegExp.test((evt.currentTarget || evt.srcElement).readyState))) { 1650 | //Reset interactive script so a script node is not held onto for 1651 | //to long. 1652 | interactiveScript = null; 1653 | 1654 | //Pull out the name of the module and the context. 1655 | var data = getScriptData(evt); 1656 | context.completeLoad(data.id); 1657 | } 1658 | }, 1659 | 1660 | /** 1661 | * Callback for script errors. 1662 | */ 1663 | onScriptError: function (evt) { 1664 | var data = getScriptData(evt); 1665 | if (!hasPathFallback(data.id)) { 1666 | return onError(makeError('scripterror', 'Script error for: ' + data.id, evt, [data.id])); 1667 | } 1668 | } 1669 | }; 1670 | 1671 | context.require = context.makeRequire(); 1672 | return context; 1673 | } 1674 | 1675 | /** 1676 | * Main entry point. 1677 | * 1678 | * If the only argument to require is a string, then the module that 1679 | * is represented by that string is fetched for the appropriate context. 1680 | * 1681 | * If the first argument is an array, then it will be treated as an array 1682 | * of dependency string names to fetch. An optional function callback can 1683 | * be specified to execute when all of those dependencies are available. 1684 | * 1685 | * Make a local req variable to help Caja compliance (it assumes things 1686 | * on a require that are not standardized), and to give a short 1687 | * name for minification/local scope use. 1688 | */ 1689 | req = requirejs = function (deps, callback, errback, optional) { 1690 | 1691 | //Find the right context, use default 1692 | var context, config, 1693 | contextName = defContextName; 1694 | 1695 | // Determine if have config object in the call. 1696 | if (!isArray(deps) && typeof deps !== 'string') { 1697 | // deps is a config object 1698 | config = deps; 1699 | if (isArray(callback)) { 1700 | // Adjust args if there are dependencies 1701 | deps = callback; 1702 | callback = errback; 1703 | errback = optional; 1704 | } else { 1705 | deps = []; 1706 | } 1707 | } 1708 | 1709 | if (config && config.context) { 1710 | contextName = config.context; 1711 | } 1712 | 1713 | context = getOwn(contexts, contextName); 1714 | if (!context) { 1715 | context = contexts[contextName] = req.s.newContext(contextName); 1716 | } 1717 | 1718 | if (config) { 1719 | context.configure(config); 1720 | } 1721 | 1722 | return context.require(deps, callback, errback); 1723 | }; 1724 | 1725 | /** 1726 | * Support require.config() to make it easier to cooperate with other 1727 | * AMD loaders on globally agreed names. 1728 | */ 1729 | req.config = function (config) { 1730 | return req(config); 1731 | }; 1732 | 1733 | /** 1734 | * Execute something after the current tick 1735 | * of the event loop. Override for other envs 1736 | * that have a better solution than setTimeout. 1737 | * @param {Function} fn function to execute later. 1738 | */ 1739 | req.nextTick = typeof setTimeout !== 'undefined' ? function (fn) { 1740 | setTimeout(fn, 4); 1741 | } : function (fn) { fn(); }; 1742 | 1743 | /** 1744 | * Export require as a global, but only if it does not already exist. 1745 | */ 1746 | if (!require) { 1747 | require = req; 1748 | } 1749 | 1750 | req.version = version; 1751 | 1752 | //Used to filter out dependencies that are already paths. 1753 | req.jsExtRegExp = /^\/|:|\?|\.js$/; 1754 | req.isBrowser = isBrowser; 1755 | s = req.s = { 1756 | contexts: contexts, 1757 | newContext: newContext 1758 | }; 1759 | 1760 | //Create default context. 1761 | req({}); 1762 | 1763 | //Exports some context-sensitive methods on global require. 1764 | each([ 1765 | 'toUrl', 1766 | 'undef', 1767 | 'defined', 1768 | 'specified' 1769 | ], function (prop) { 1770 | //Reference from contexts instead of early binding to default context, 1771 | //so that during builds, the latest instance of the default context 1772 | //with its config gets used. 1773 | req[prop] = function () { 1774 | var ctx = contexts[defContextName]; 1775 | return ctx.require[prop].apply(ctx, arguments); 1776 | }; 1777 | }); 1778 | 1779 | if (isBrowser) { 1780 | head = s.head = document.getElementsByTagName('head')[0]; 1781 | //If BASE tag is in play, using appendChild is a problem for IE6. 1782 | //When that browser dies, this can be removed. Details in this jQuery bug: 1783 | //http://dev.jquery.com/ticket/2709 1784 | baseElement = document.getElementsByTagName('base')[0]; 1785 | if (baseElement) { 1786 | head = s.head = baseElement.parentNode; 1787 | } 1788 | } 1789 | 1790 | /** 1791 | * Any errors that require explicitly generates will be passed to this 1792 | * function. Intercept/override it if you want custom error handling. 1793 | * @param {Error} err the error object. 1794 | */ 1795 | req.onError = defaultOnError; 1796 | 1797 | /** 1798 | * Creates the node for the load command. Only used in browser envs. 1799 | */ 1800 | req.createNode = function (config, moduleName, url) { 1801 | var node = config.xhtml ? 1802 | document.createElementNS('http://www.w3.org/1999/xhtml', 'html:script') : 1803 | document.createElement('script'); 1804 | node.type = config.scriptType || 'text/javascript'; 1805 | node.charset = 'utf-8'; 1806 | node.async = true; 1807 | return node; 1808 | }; 1809 | 1810 | /** 1811 | * Does the request to load a module for the browser case. 1812 | * Make this a separate function to allow other environments 1813 | * to override it. 1814 | * 1815 | * @param {Object} context the require context to find state. 1816 | * @param {String} moduleName the name of the module. 1817 | * @param {Object} url the URL to the module. 1818 | */ 1819 | req.load = function (context, moduleName, url) { 1820 | var config = (context && context.config) || {}, 1821 | node; 1822 | if (isBrowser) { 1823 | //In the browser so use a script tag 1824 | node = req.createNode(config, moduleName, url); 1825 | 1826 | node.setAttribute('data-requirecontext', context.contextName); 1827 | node.setAttribute('data-requiremodule', moduleName); 1828 | 1829 | //Set up load listener. Test attachEvent first because IE9 has 1830 | //a subtle issue in its addEventListener and script onload firings 1831 | //that do not match the behavior of all other browsers with 1832 | //addEventListener support, which fire the onload event for a 1833 | //script right after the script execution. See: 1834 | //https://connect.microsoft.com/IE/feedback/details/648057/script-onload-event-is-not-fired-immediately-after-script-execution 1835 | //UNFORTUNATELY Opera implements attachEvent but does not follow the script 1836 | //script execution mode. 1837 | if (node.attachEvent && 1838 | //Check if node.attachEvent is artificially added by custom script or 1839 | //natively supported by browser 1840 | //read https://github.com/jrburke/requirejs/issues/187 1841 | //if we can NOT find [native code] then it must NOT natively supported. 1842 | //in IE8, node.attachEvent does not have toString() 1843 | //Note the test for "[native code" with no closing brace, see: 1844 | //https://github.com/jrburke/requirejs/issues/273 1845 | !(node.attachEvent.toString && node.attachEvent.toString().indexOf('[native code') < 0) && 1846 | !isOpera) { 1847 | //Probably IE. IE (at least 6-8) do not fire 1848 | //script onload right after executing the script, so 1849 | //we cannot tie the anonymous define call to a name. 1850 | //However, IE reports the script as being in 'interactive' 1851 | //readyState at the time of the define call. 1852 | useInteractive = true; 1853 | 1854 | node.attachEvent('onreadystatechange', context.onScriptLoad); 1855 | //It would be great to add an error handler here to catch 1856 | //404s in IE9+. However, onreadystatechange will fire before 1857 | //the error handler, so that does not help. If addEventListener 1858 | //is used, then IE will fire error before load, but we cannot 1859 | //use that pathway given the connect.microsoft.com issue 1860 | //mentioned above about not doing the 'script execute, 1861 | //then fire the script load event listener before execute 1862 | //next script' that other browsers do. 1863 | //Best hope: IE10 fixes the issues, 1864 | //and then destroys all installs of IE 6-9. 1865 | //node.attachEvent('onerror', context.onScriptError); 1866 | } else { 1867 | node.addEventListener('load', context.onScriptLoad, false); 1868 | node.addEventListener('error', context.onScriptError, false); 1869 | } 1870 | node.src = url; 1871 | 1872 | //For some cache cases in IE 6-8, the script executes before the end 1873 | //of the appendChild execution, so to tie an anonymous define 1874 | //call to the module name (which is stored on the node), hold on 1875 | //to a reference to this node, but clear after the DOM insertion. 1876 | currentlyAddingScript = node; 1877 | if (baseElement) { 1878 | head.insertBefore(node, baseElement); 1879 | } else { 1880 | head.appendChild(node); 1881 | } 1882 | currentlyAddingScript = null; 1883 | 1884 | return node; 1885 | } else if (isWebWorker) { 1886 | try { 1887 | //In a web worker, use importScripts. This is not a very 1888 | //efficient use of importScripts, importScripts will block until 1889 | //its script is downloaded and evaluated. However, if web workers 1890 | //are in play, the expectation that a build has been done so that 1891 | //only one script needs to be loaded anyway. This may need to be 1892 | //reevaluated if other use cases become common. 1893 | importScripts(url); 1894 | 1895 | //Account for anonymous modules 1896 | context.completeLoad(moduleName); 1897 | } catch (e) { 1898 | context.onError(makeError('importscripts', 1899 | 'importScripts failed for ' + 1900 | moduleName + ' at ' + url, 1901 | e, 1902 | [moduleName])); 1903 | } 1904 | } 1905 | }; 1906 | 1907 | function getInteractiveScript() { 1908 | if (interactiveScript && interactiveScript.readyState === 'interactive') { 1909 | return interactiveScript; 1910 | } 1911 | 1912 | eachReverse(scripts(), function (script) { 1913 | if (script.readyState === 'interactive') { 1914 | return (interactiveScript = script); 1915 | } 1916 | }); 1917 | return interactiveScript; 1918 | } 1919 | 1920 | //Look for a data-main script attribute, which could also adjust the baseUrl. 1921 | if (isBrowser) { 1922 | //Figure out baseUrl. Get it from the script tag with require.js in it. 1923 | eachReverse(scripts(), function (script) { 1924 | //Set the 'head' where we can append children by 1925 | //using the script's parent. 1926 | if (!head) { 1927 | head = script.parentNode; 1928 | } 1929 | 1930 | //Look for a data-main attribute to set main script for the page 1931 | //to load. If it is there, the path to data main becomes the 1932 | //baseUrl, if it is not already set. 1933 | dataMain = script.getAttribute('data-main'); 1934 | if (dataMain) { 1935 | //Preserve dataMain in case it is a path (i.e. contains '?') 1936 | mainScript = dataMain; 1937 | 1938 | //Set final baseUrl if there is not already an explicit one. 1939 | if (!cfg.baseUrl) { 1940 | //Pull off the directory of data-main for use as the 1941 | //baseUrl. 1942 | src = mainScript.split('/'); 1943 | mainScript = src.pop(); 1944 | subPath = src.length ? src.join('/') + '/' : './'; 1945 | 1946 | cfg.baseUrl = subPath; 1947 | } 1948 | 1949 | //Strip off any trailing .js since mainScript is now 1950 | //like a module name. 1951 | mainScript = mainScript.replace(jsSuffixRegExp, ''); 1952 | 1953 | //If mainScript is still a path, fall back to dataMain 1954 | if (req.jsExtRegExp.test(mainScript)) { 1955 | mainScript = dataMain; 1956 | } 1957 | 1958 | //Put the data-main script in the files to load. 1959 | cfg.deps = cfg.deps ? cfg.deps.concat(mainScript) : [mainScript]; 1960 | 1961 | return true; 1962 | } 1963 | }); 1964 | } 1965 | 1966 | /** 1967 | * The function that handles definitions of modules. Differs from 1968 | * require() in that a string for the module should be the first argument, 1969 | * and the function to execute after dependencies are loaded should 1970 | * return a value to define the module corresponding to the first argument's 1971 | * name. 1972 | */ 1973 | define = function (name, deps, callback) { 1974 | var node, context; 1975 | 1976 | //Allow for anonymous modules 1977 | if (typeof name !== 'string') { 1978 | //Adjust args appropriately 1979 | callback = deps; 1980 | deps = name; 1981 | name = null; 1982 | } 1983 | 1984 | //This module may not have dependencies 1985 | if (!isArray(deps)) { 1986 | callback = deps; 1987 | deps = null; 1988 | } 1989 | 1990 | //If no name, and callback is a function, then figure out if it a 1991 | //CommonJS thing with dependencies. 1992 | if (!deps && isFunction(callback)) { 1993 | deps = []; 1994 | //Remove comments from the callback string, 1995 | //look for require calls, and pull them into the dependencies, 1996 | //but only if there are function args. 1997 | if (callback.length) { 1998 | callback 1999 | .toString() 2000 | .replace(commentRegExp, '') 2001 | .replace(cjsRequireRegExp, function (match, dep) { 2002 | deps.push(dep); 2003 | }); 2004 | 2005 | //May be a CommonJS thing even without require calls, but still 2006 | //could use exports, and module. Avoid doing exports and module 2007 | //work though if it just needs require. 2008 | //REQUIRES the function to expect the CommonJS variables in the 2009 | //order listed below. 2010 | deps = (callback.length === 1 ? ['require'] : ['require', 'exports', 'module']).concat(deps); 2011 | } 2012 | } 2013 | 2014 | //If in IE 6-8 and hit an anonymous define() call, do the interactive 2015 | //work. 2016 | if (useInteractive) { 2017 | node = currentlyAddingScript || getInteractiveScript(); 2018 | if (node) { 2019 | if (!name) { 2020 | name = node.getAttribute('data-requiremodule'); 2021 | } 2022 | context = contexts[node.getAttribute('data-requirecontext')]; 2023 | } 2024 | } 2025 | 2026 | //Always save off evaluating the def call until the script onload handler. 2027 | //This allows multiple modules to be in a file without prematurely 2028 | //tracing dependencies, and allows for anonymous module support, 2029 | //where the module name is not known until the script onload event 2030 | //occurs. If no context, use the global queue, and get it processed 2031 | //in the onscript load callback. 2032 | (context ? context.defQueue : globalDefQueue).push([name, deps, callback]); 2033 | }; 2034 | 2035 | define.amd = { 2036 | jQuery: true 2037 | }; 2038 | 2039 | 2040 | /** 2041 | * Executes the text. Normally just uses eval, but can be modified 2042 | * to use a better, environment-specific call. Only used for transpiling 2043 | * loader plugins, not for plain JS modules. 2044 | * @param {String} text the text to execute/evaluate. 2045 | */ 2046 | req.exec = function (text) { 2047 | /*jslint evil: true */ 2048 | return eval(text); 2049 | }; 2050 | 2051 | //Set up with config info. 2052 | req(cfg); 2053 | }(this)); 2054 | -------------------------------------------------------------------------------- /lib/underscore/underscore.js: -------------------------------------------------------------------------------- 1 | // Underscore.js 1.4.4 2 | // =================== 3 | 4 | // > http://underscorejs.org 5 | // > (c) 2009-2013 Jeremy Ashkenas, DocumentCloud Inc. 6 | // > Underscore may be freely distributed under the MIT license. 7 | 8 | // Baseline setup 9 | // -------------- 10 | (function() { 11 | 12 | // Establish the root object, `window` in the browser, or `global` on the server. 13 | var root = this; 14 | 15 | // Save the previous value of the `_` variable. 16 | var previousUnderscore = root._; 17 | 18 | // Establish the object that gets returned to break out of a loop iteration. 19 | var breaker = {}; 20 | 21 | // Save bytes in the minified (but not gzipped) version: 22 | var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype; 23 | 24 | // Create quick reference variables for speed access to core prototypes. 25 | var push = ArrayProto.push, 26 | slice = ArrayProto.slice, 27 | concat = ArrayProto.concat, 28 | toString = ObjProto.toString, 29 | hasOwnProperty = ObjProto.hasOwnProperty; 30 | 31 | // All **ECMAScript 5** native function implementations that we hope to use 32 | // are declared here. 33 | var 34 | nativeForEach = ArrayProto.forEach, 35 | nativeMap = ArrayProto.map, 36 | nativeReduce = ArrayProto.reduce, 37 | nativeReduceRight = ArrayProto.reduceRight, 38 | nativeFilter = ArrayProto.filter, 39 | nativeEvery = ArrayProto.every, 40 | nativeSome = ArrayProto.some, 41 | nativeIndexOf = ArrayProto.indexOf, 42 | nativeLastIndexOf = ArrayProto.lastIndexOf, 43 | nativeIsArray = Array.isArray, 44 | nativeKeys = Object.keys, 45 | nativeBind = FuncProto.bind; 46 | 47 | // Create a safe reference to the Underscore object for use below. 48 | var _ = function(obj) { 49 | if (obj instanceof _) return obj; 50 | if (!(this instanceof _)) return new _(obj); 51 | this._wrapped = obj; 52 | }; 53 | 54 | // Export the Underscore object for **Node.js**, with 55 | // backwards-compatibility for the old `require()` API. If we're in 56 | // the browser, add `_` as a global object via a string identifier, 57 | // for Closure Compiler "advanced" mode. 58 | if (typeof exports !== 'undefined') { 59 | if (typeof module !== 'undefined' && module.exports) { 60 | exports = module.exports = _; 61 | } 62 | exports._ = _; 63 | } else { 64 | root._ = _; 65 | } 66 | 67 | // Current version. 68 | _.VERSION = '1.4.4'; 69 | 70 | // Collection Functions 71 | // -------------------- 72 | 73 | // The cornerstone, an `each` implementation, aka `forEach`. 74 | // Handles objects with the built-in `forEach`, arrays, and raw objects. 75 | // Delegates to **ECMAScript 5**'s native `forEach` if available. 76 | var each = _.each = _.forEach = function(obj, iterator, context) { 77 | if (obj == null) return; 78 | if (nativeForEach && obj.forEach === nativeForEach) { 79 | obj.forEach(iterator, context); 80 | } else if (obj.length === +obj.length) { 81 | for (var i = 0, l = obj.length; i < l; i++) { 82 | if (iterator.call(context, obj[i], i, obj) === breaker) return; 83 | } 84 | } else { 85 | for (var key in obj) { 86 | if (_.has(obj, key)) { 87 | if (iterator.call(context, obj[key], key, obj) === breaker) return; 88 | } 89 | } 90 | } 91 | }; 92 | 93 | // Return the results of applying the iterator to each element. 94 | // Delegates to **ECMAScript 5**'s native `map` if available. 95 | _.map = _.collect = function(obj, iterator, context) { 96 | var results = []; 97 | if (obj == null) return results; 98 | if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context); 99 | each(obj, function(value, index, list) { 100 | results[results.length] = iterator.call(context, value, index, list); 101 | }); 102 | return results; 103 | }; 104 | 105 | var reduceError = 'Reduce of empty array with no initial value'; 106 | 107 | // **Reduce** builds up a single result from a list of values, aka `inject`, 108 | // or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available. 109 | _.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) { 110 | var initial = arguments.length > 2; 111 | if (obj == null) obj = []; 112 | if (nativeReduce && obj.reduce === nativeReduce) { 113 | if (context) iterator = _.bind(iterator, context); 114 | return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator); 115 | } 116 | each(obj, function(value, index, list) { 117 | if (!initial) { 118 | memo = value; 119 | initial = true; 120 | } else { 121 | memo = iterator.call(context, memo, value, index, list); 122 | } 123 | }); 124 | if (!initial) throw new TypeError(reduceError); 125 | return memo; 126 | }; 127 | 128 | // The right-associative version of reduce, also known as `foldr`. 129 | // Delegates to **ECMAScript 5**'s native `reduceRight` if available. 130 | _.reduceRight = _.foldr = function(obj, iterator, memo, context) { 131 | var initial = arguments.length > 2; 132 | if (obj == null) obj = []; 133 | if (nativeReduceRight && obj.reduceRight === nativeReduceRight) { 134 | if (context) iterator = _.bind(iterator, context); 135 | return initial ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator); 136 | } 137 | var length = obj.length; 138 | if (length !== +length) { 139 | var keys = _.keys(obj); 140 | length = keys.length; 141 | } 142 | each(obj, function(value, index, list) { 143 | index = keys ? keys[--length] : --length; 144 | if (!initial) { 145 | memo = obj[index]; 146 | initial = true; 147 | } else { 148 | memo = iterator.call(context, memo, obj[index], index, list); 149 | } 150 | }); 151 | if (!initial) throw new TypeError(reduceError); 152 | return memo; 153 | }; 154 | 155 | // Return the first value which passes a truth test. Aliased as `detect`. 156 | _.find = _.detect = function(obj, iterator, context) { 157 | var result; 158 | any(obj, function(value, index, list) { 159 | if (iterator.call(context, value, index, list)) { 160 | result = value; 161 | return true; 162 | } 163 | }); 164 | return result; 165 | }; 166 | 167 | // Return all the elements that pass a truth test. 168 | // Delegates to **ECMAScript 5**'s native `filter` if available. 169 | // Aliased as `select`. 170 | _.filter = _.select = function(obj, iterator, context) { 171 | var results = []; 172 | if (obj == null) return results; 173 | if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context); 174 | each(obj, function(value, index, list) { 175 | if (iterator.call(context, value, index, list)) results[results.length] = value; 176 | }); 177 | return results; 178 | }; 179 | 180 | // Return all the elements for which a truth test fails. 181 | _.reject = function(obj, iterator, context) { 182 | return _.filter(obj, function(value, index, list) { 183 | return !iterator.call(context, value, index, list); 184 | }, context); 185 | }; 186 | 187 | // Determine whether all of the elements match a truth test. 188 | // Delegates to **ECMAScript 5**'s native `every` if available. 189 | // Aliased as `all`. 190 | _.every = _.all = function(obj, iterator, context) { 191 | iterator || (iterator = _.identity); 192 | var result = true; 193 | if (obj == null) return result; 194 | if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context); 195 | each(obj, function(value, index, list) { 196 | if (!(result = result && iterator.call(context, value, index, list))) return breaker; 197 | }); 198 | return !!result; 199 | }; 200 | 201 | // Determine if at least one element in the object matches a truth test. 202 | // Delegates to **ECMAScript 5**'s native `some` if available. 203 | // Aliased as `any`. 204 | var any = _.some = _.any = function(obj, iterator, context) { 205 | iterator || (iterator = _.identity); 206 | var result = false; 207 | if (obj == null) return result; 208 | if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context); 209 | each(obj, function(value, index, list) { 210 | if (result || (result = iterator.call(context, value, index, list))) return breaker; 211 | }); 212 | return !!result; 213 | }; 214 | 215 | // Determine if the array or object contains a given value (using `===`). 216 | // Aliased as `include`. 217 | _.contains = _.include = function(obj, target) { 218 | if (obj == null) return false; 219 | if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1; 220 | return any(obj, function(value) { 221 | return value === target; 222 | }); 223 | }; 224 | 225 | // Invoke a method (with arguments) on every item in a collection. 226 | _.invoke = function(obj, method) { 227 | var args = slice.call(arguments, 2); 228 | var isFunc = _.isFunction(method); 229 | return _.map(obj, function(value) { 230 | return (isFunc ? method : value[method]).apply(value, args); 231 | }); 232 | }; 233 | 234 | // Convenience version of a common use case of `map`: fetching a property. 235 | _.pluck = function(obj, key) { 236 | return _.map(obj, function(value){ return value[key]; }); 237 | }; 238 | 239 | // Convenience version of a common use case of `filter`: selecting only objects 240 | // containing specific `key:value` pairs. 241 | _.where = function(obj, attrs, first) { 242 | if (_.isEmpty(attrs)) return first ? null : []; 243 | return _[first ? 'find' : 'filter'](obj, function(value) { 244 | for (var key in attrs) { 245 | if (attrs[key] !== value[key]) return false; 246 | } 247 | return true; 248 | }); 249 | }; 250 | 251 | // Convenience version of a common use case of `find`: getting the first object 252 | // containing specific `key:value` pairs. 253 | _.findWhere = function(obj, attrs) { 254 | return _.where(obj, attrs, true); 255 | }; 256 | 257 | // Return the maximum element or (element-based computation). 258 | // Can't optimize arrays of integers longer than 65,535 elements. 259 | // See: https://bugs.webkit.org/show_bug.cgi?id=80797 260 | _.max = function(obj, iterator, context) { 261 | if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) { 262 | return Math.max.apply(Math, obj); 263 | } 264 | if (!iterator && _.isEmpty(obj)) return -Infinity; 265 | var result = {computed : -Infinity, value: -Infinity}; 266 | each(obj, function(value, index, list) { 267 | var computed = iterator ? iterator.call(context, value, index, list) : value; 268 | computed >= result.computed && (result = {value : value, computed : computed}); 269 | }); 270 | return result.value; 271 | }; 272 | 273 | // Return the minimum element (or element-based computation). 274 | _.min = function(obj, iterator, context) { 275 | if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) { 276 | return Math.min.apply(Math, obj); 277 | } 278 | if (!iterator && _.isEmpty(obj)) return Infinity; 279 | var result = {computed : Infinity, value: Infinity}; 280 | each(obj, function(value, index, list) { 281 | var computed = iterator ? iterator.call(context, value, index, list) : value; 282 | computed < result.computed && (result = {value : value, computed : computed}); 283 | }); 284 | return result.value; 285 | }; 286 | 287 | // Shuffle an array. 288 | _.shuffle = function(obj) { 289 | var rand; 290 | var index = 0; 291 | var shuffled = []; 292 | each(obj, function(value) { 293 | rand = _.random(index++); 294 | shuffled[index - 1] = shuffled[rand]; 295 | shuffled[rand] = value; 296 | }); 297 | return shuffled; 298 | }; 299 | 300 | // An internal function to generate lookup iterators. 301 | var lookupIterator = function(value) { 302 | return _.isFunction(value) ? value : function(obj){ return obj[value]; }; 303 | }; 304 | 305 | // Sort the object's values by a criterion produced by an iterator. 306 | _.sortBy = function(obj, value, context) { 307 | var iterator = lookupIterator(value); 308 | return _.pluck(_.map(obj, function(value, index, list) { 309 | return { 310 | value : value, 311 | index : index, 312 | criteria : iterator.call(context, value, index, list) 313 | }; 314 | }).sort(function(left, right) { 315 | var a = left.criteria; 316 | var b = right.criteria; 317 | if (a !== b) { 318 | if (a > b || a === void 0) return 1; 319 | if (a < b || b === void 0) return -1; 320 | } 321 | return left.index < right.index ? -1 : 1; 322 | }), 'value'); 323 | }; 324 | 325 | // An internal function used for aggregate "group by" operations. 326 | var group = function(obj, value, context, behavior) { 327 | var result = {}; 328 | var iterator = lookupIterator(value || _.identity); 329 | each(obj, function(value, index) { 330 | var key = iterator.call(context, value, index, obj); 331 | behavior(result, key, value); 332 | }); 333 | return result; 334 | }; 335 | 336 | // Groups the object's values by a criterion. Pass either a string attribute 337 | // to group by, or a function that returns the criterion. 338 | _.groupBy = function(obj, value, context) { 339 | return group(obj, value, context, function(result, key, value) { 340 | (_.has(result, key) ? result[key] : (result[key] = [])).push(value); 341 | }); 342 | }; 343 | 344 | // Counts instances of an object that group by a certain criterion. Pass 345 | // either a string attribute to count by, or a function that returns the 346 | // criterion. 347 | _.countBy = function(obj, value, context) { 348 | return group(obj, value, context, function(result, key) { 349 | if (!_.has(result, key)) result[key] = 0; 350 | result[key]++; 351 | }); 352 | }; 353 | 354 | // Use a comparator function to figure out the smallest index at which 355 | // an object should be inserted so as to maintain order. Uses binary search. 356 | _.sortedIndex = function(array, obj, iterator, context) { 357 | iterator = iterator == null ? _.identity : lookupIterator(iterator); 358 | var value = iterator.call(context, obj); 359 | var low = 0, high = array.length; 360 | while (low < high) { 361 | var mid = (low + high) >>> 1; 362 | iterator.call(context, array[mid]) < value ? low = mid + 1 : high = mid; 363 | } 364 | return low; 365 | }; 366 | 367 | // Safely convert anything iterable into a real, live array. 368 | _.toArray = function(obj) { 369 | if (!obj) return []; 370 | if (_.isArray(obj)) return slice.call(obj); 371 | if (obj.length === +obj.length) return _.map(obj, _.identity); 372 | return _.values(obj); 373 | }; 374 | 375 | // Return the number of elements in an object. 376 | _.size = function(obj) { 377 | if (obj == null) return 0; 378 | return (obj.length === +obj.length) ? obj.length : _.keys(obj).length; 379 | }; 380 | 381 | // Array Functions 382 | // --------------- 383 | 384 | // Get the first element of an array. Passing **n** will return the first N 385 | // values in the array. Aliased as `head` and `take`. The **guard** check 386 | // allows it to work with `_.map`. 387 | _.first = _.head = _.take = function(array, n, guard) { 388 | if (array == null) return void 0; 389 | return (n != null) && !guard ? slice.call(array, 0, n) : array[0]; 390 | }; 391 | 392 | // Returns everything but the last entry of the array. Especially useful on 393 | // the arguments object. Passing **n** will return all the values in 394 | // the array, excluding the last N. The **guard** check allows it to work with 395 | // `_.map`. 396 | _.initial = function(array, n, guard) { 397 | return slice.call(array, 0, array.length - ((n == null) || guard ? 1 : n)); 398 | }; 399 | 400 | // Get the last element of an array. Passing **n** will return the last N 401 | // values in the array. The **guard** check allows it to work with `_.map`. 402 | _.last = function(array, n, guard) { 403 | if (array == null) return void 0; 404 | if ((n != null) && !guard) { 405 | return slice.call(array, Math.max(array.length - n, 0)); 406 | } else { 407 | return array[array.length - 1]; 408 | } 409 | }; 410 | 411 | // Returns everything but the first entry of the array. Aliased as `tail` and `drop`. 412 | // Especially useful on the arguments object. Passing an **n** will return 413 | // the rest N values in the array. The **guard** 414 | // check allows it to work with `_.map`. 415 | _.rest = _.tail = _.drop = function(array, n, guard) { 416 | return slice.call(array, (n == null) || guard ? 1 : n); 417 | }; 418 | 419 | // Trim out all falsy values from an array. 420 | _.compact = function(array) { 421 | return _.filter(array, _.identity); 422 | }; 423 | 424 | // Internal implementation of a recursive `flatten` function. 425 | var flatten = function(input, shallow, output) { 426 | each(input, function(value) { 427 | if (_.isArray(value)) { 428 | shallow ? push.apply(output, value) : flatten(value, shallow, output); 429 | } else { 430 | output.push(value); 431 | } 432 | }); 433 | return output; 434 | }; 435 | 436 | // Return a completely flattened version of an array. 437 | _.flatten = function(array, shallow) { 438 | return flatten(array, shallow, []); 439 | }; 440 | 441 | // Return a version of the array that does not contain the specified value(s). 442 | _.without = function(array) { 443 | return _.difference(array, slice.call(arguments, 1)); 444 | }; 445 | 446 | // Produce a duplicate-free version of the array. If the array has already 447 | // been sorted, you have the option of using a faster algorithm. 448 | // Aliased as `unique`. 449 | _.uniq = _.unique = function(array, isSorted, iterator, context) { 450 | if (_.isFunction(isSorted)) { 451 | context = iterator; 452 | iterator = isSorted; 453 | isSorted = false; 454 | } 455 | var initial = iterator ? _.map(array, iterator, context) : array; 456 | var results = []; 457 | var seen = []; 458 | each(initial, function(value, index) { 459 | if (isSorted ? (!index || seen[seen.length - 1] !== value) : !_.contains(seen, value)) { 460 | seen.push(value); 461 | results.push(array[index]); 462 | } 463 | }); 464 | return results; 465 | }; 466 | 467 | // Produce an array that contains the union: each distinct element from all of 468 | // the passed-in arrays. 469 | _.union = function() { 470 | return _.uniq(concat.apply(ArrayProto, arguments)); 471 | }; 472 | 473 | // Produce an array that contains every item shared between all the 474 | // passed-in arrays. 475 | _.intersection = function(array) { 476 | var rest = slice.call(arguments, 1); 477 | return _.filter(_.uniq(array), function(item) { 478 | return _.every(rest, function(other) { 479 | return _.indexOf(other, item) >= 0; 480 | }); 481 | }); 482 | }; 483 | 484 | // Take the difference between one array and a number of other arrays. 485 | // Only the elements present in just the first array will remain. 486 | _.difference = function(array) { 487 | var rest = concat.apply(ArrayProto, slice.call(arguments, 1)); 488 | return _.filter(array, function(value){ return !_.contains(rest, value); }); 489 | }; 490 | 491 | // Zip together multiple lists into a single array -- elements that share 492 | // an index go together. 493 | _.zip = function() { 494 | var args = slice.call(arguments); 495 | var length = _.max(_.pluck(args, 'length')); 496 | var results = new Array(length); 497 | for (var i = 0; i < length; i++) { 498 | results[i] = _.pluck(args, "" + i); 499 | } 500 | return results; 501 | }; 502 | 503 | // Converts lists into objects. Pass either a single array of `[key, value]` 504 | // pairs, or two parallel arrays of the same length -- one of keys, and one of 505 | // the corresponding values. 506 | _.object = function(list, values) { 507 | if (list == null) return {}; 508 | var result = {}; 509 | for (var i = 0, l = list.length; i < l; i++) { 510 | if (values) { 511 | result[list[i]] = values[i]; 512 | } else { 513 | result[list[i][0]] = list[i][1]; 514 | } 515 | } 516 | return result; 517 | }; 518 | 519 | // If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**), 520 | // we need this function. Return the position of the first occurrence of an 521 | // item in an array, or -1 if the item is not included in the array. 522 | // Delegates to **ECMAScript 5**'s native `indexOf` if available. 523 | // If the array is large and already in sort order, pass `true` 524 | // for **isSorted** to use binary search. 525 | _.indexOf = function(array, item, isSorted) { 526 | if (array == null) return -1; 527 | var i = 0, l = array.length; 528 | if (isSorted) { 529 | if (typeof isSorted == 'number') { 530 | i = (isSorted < 0 ? Math.max(0, l + isSorted) : isSorted); 531 | } else { 532 | i = _.sortedIndex(array, item); 533 | return array[i] === item ? i : -1; 534 | } 535 | } 536 | if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item, isSorted); 537 | for (; i < l; i++) if (array[i] === item) return i; 538 | return -1; 539 | }; 540 | 541 | // Delegates to **ECMAScript 5**'s native `lastIndexOf` if available. 542 | _.lastIndexOf = function(array, item, from) { 543 | if (array == null) return -1; 544 | var hasIndex = from != null; 545 | if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) { 546 | return hasIndex ? array.lastIndexOf(item, from) : array.lastIndexOf(item); 547 | } 548 | var i = (hasIndex ? from : array.length); 549 | while (i--) if (array[i] === item) return i; 550 | return -1; 551 | }; 552 | 553 | // Generate an integer Array containing an arithmetic progression. A port of 554 | // the native Python `range()` function. See 555 | // [the Python documentation](http://docs.python.org/library/functions.html#range). 556 | _.range = function(start, stop, step) { 557 | if (arguments.length <= 1) { 558 | stop = start || 0; 559 | start = 0; 560 | } 561 | step = arguments[2] || 1; 562 | 563 | var len = Math.max(Math.ceil((stop - start) / step), 0); 564 | var idx = 0; 565 | var range = new Array(len); 566 | 567 | while(idx < len) { 568 | range[idx++] = start; 569 | start += step; 570 | } 571 | 572 | return range; 573 | }; 574 | 575 | // Function (ahem) Functions 576 | // ------------------ 577 | 578 | // Create a function bound to a given object (assigning `this`, and arguments, 579 | // optionally). Delegates to **ECMAScript 5**'s native `Function.bind` if 580 | // available. 581 | _.bind = function(func, context) { 582 | if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1)); 583 | var args = slice.call(arguments, 2); 584 | return function() { 585 | return func.apply(context, args.concat(slice.call(arguments))); 586 | }; 587 | }; 588 | 589 | // Partially apply a function by creating a version that has had some of its 590 | // arguments pre-filled, without changing its dynamic `this` context. 591 | _.partial = function(func) { 592 | var args = slice.call(arguments, 1); 593 | return function() { 594 | return func.apply(this, args.concat(slice.call(arguments))); 595 | }; 596 | }; 597 | 598 | // Bind all of an object's methods to that object. Useful for ensuring that 599 | // all callbacks defined on an object belong to it. 600 | _.bindAll = function(obj) { 601 | var funcs = slice.call(arguments, 1); 602 | if (funcs.length === 0) funcs = _.functions(obj); 603 | each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); }); 604 | return obj; 605 | }; 606 | 607 | // Memoize an expensive function by storing its results. 608 | _.memoize = function(func, hasher) { 609 | var memo = {}; 610 | hasher || (hasher = _.identity); 611 | return function() { 612 | var key = hasher.apply(this, arguments); 613 | return _.has(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments)); 614 | }; 615 | }; 616 | 617 | // Delays a function for the given number of milliseconds, and then calls 618 | // it with the arguments supplied. 619 | _.delay = function(func, wait) { 620 | var args = slice.call(arguments, 2); 621 | return setTimeout(function(){ return func.apply(null, args); }, wait); 622 | }; 623 | 624 | // Defers a function, scheduling it to run after the current call stack has 625 | // cleared. 626 | _.defer = function(func) { 627 | return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1))); 628 | }; 629 | 630 | // Returns a function, that, when invoked, will only be triggered at most once 631 | // during a given window of time. 632 | _.throttle = function(func, wait) { 633 | var context, args, timeout, result; 634 | var previous = 0; 635 | var later = function() { 636 | previous = new Date; 637 | timeout = null; 638 | result = func.apply(context, args); 639 | }; 640 | return function() { 641 | var now = new Date; 642 | var remaining = wait - (now - previous); 643 | context = this; 644 | args = arguments; 645 | if (remaining <= 0) { 646 | clearTimeout(timeout); 647 | timeout = null; 648 | previous = now; 649 | result = func.apply(context, args); 650 | } else if (!timeout) { 651 | timeout = setTimeout(later, remaining); 652 | } 653 | return result; 654 | }; 655 | }; 656 | 657 | // Returns a function, that, as long as it continues to be invoked, will not 658 | // be triggered. The function will be called after it stops being called for 659 | // N milliseconds. If `immediate` is passed, trigger the function on the 660 | // leading edge, instead of the trailing. 661 | _.debounce = function(func, wait, immediate) { 662 | var timeout, result; 663 | return function() { 664 | var context = this, args = arguments; 665 | var later = function() { 666 | timeout = null; 667 | if (!immediate) result = func.apply(context, args); 668 | }; 669 | var callNow = immediate && !timeout; 670 | clearTimeout(timeout); 671 | timeout = setTimeout(later, wait); 672 | if (callNow) result = func.apply(context, args); 673 | return result; 674 | }; 675 | }; 676 | 677 | // Returns a function that will be executed at most one time, no matter how 678 | // often you call it. Useful for lazy initialization. 679 | _.once = function(func) { 680 | var ran = false, memo; 681 | return function() { 682 | if (ran) return memo; 683 | ran = true; 684 | memo = func.apply(this, arguments); 685 | func = null; 686 | return memo; 687 | }; 688 | }; 689 | 690 | // Returns the first function passed as an argument to the second, 691 | // allowing you to adjust arguments, run code before and after, and 692 | // conditionally execute the original function. 693 | _.wrap = function(func, wrapper) { 694 | return function() { 695 | var args = [func]; 696 | push.apply(args, arguments); 697 | return wrapper.apply(this, args); 698 | }; 699 | }; 700 | 701 | // Returns a function that is the composition of a list of functions, each 702 | // consuming the return value of the function that follows. 703 | _.compose = function() { 704 | var funcs = arguments; 705 | return function() { 706 | var args = arguments; 707 | for (var i = funcs.length - 1; i >= 0; i--) { 708 | args = [funcs[i].apply(this, args)]; 709 | } 710 | return args[0]; 711 | }; 712 | }; 713 | 714 | // Returns a function that will only be executed after being called N times. 715 | _.after = function(times, func) { 716 | if (times <= 0) return func(); 717 | return function() { 718 | if (--times < 1) { 719 | return func.apply(this, arguments); 720 | } 721 | }; 722 | }; 723 | 724 | // Object Functions 725 | // ---------------- 726 | 727 | // Retrieve the names of an object's properties. 728 | // Delegates to **ECMAScript 5**'s native `Object.keys` 729 | _.keys = nativeKeys || function(obj) { 730 | if (obj !== Object(obj)) throw new TypeError('Invalid object'); 731 | var keys = []; 732 | for (var key in obj) if (_.has(obj, key)) keys[keys.length] = key; 733 | return keys; 734 | }; 735 | 736 | // Retrieve the values of an object's properties. 737 | _.values = function(obj) { 738 | var values = []; 739 | for (var key in obj) if (_.has(obj, key)) values.push(obj[key]); 740 | return values; 741 | }; 742 | 743 | // Convert an object into a list of `[key, value]` pairs. 744 | _.pairs = function(obj) { 745 | var pairs = []; 746 | for (var key in obj) if (_.has(obj, key)) pairs.push([key, obj[key]]); 747 | return pairs; 748 | }; 749 | 750 | // Invert the keys and values of an object. The values must be serializable. 751 | _.invert = function(obj) { 752 | var result = {}; 753 | for (var key in obj) if (_.has(obj, key)) result[obj[key]] = key; 754 | return result; 755 | }; 756 | 757 | // Return a sorted list of the function names available on the object. 758 | // Aliased as `methods` 759 | _.functions = _.methods = function(obj) { 760 | var names = []; 761 | for (var key in obj) { 762 | if (_.isFunction(obj[key])) names.push(key); 763 | } 764 | return names.sort(); 765 | }; 766 | 767 | // Extend a given object with all the properties in passed-in object(s). 768 | _.extend = function(obj) { 769 | each(slice.call(arguments, 1), function(source) { 770 | if (source) { 771 | for (var prop in source) { 772 | obj[prop] = source[prop]; 773 | } 774 | } 775 | }); 776 | return obj; 777 | }; 778 | 779 | // Return a copy of the object only containing the whitelisted properties. 780 | _.pick = function(obj) { 781 | var copy = {}; 782 | var keys = concat.apply(ArrayProto, slice.call(arguments, 1)); 783 | each(keys, function(key) { 784 | if (key in obj) copy[key] = obj[key]; 785 | }); 786 | return copy; 787 | }; 788 | 789 | // Return a copy of the object without the blacklisted properties. 790 | _.omit = function(obj) { 791 | var copy = {}; 792 | var keys = concat.apply(ArrayProto, slice.call(arguments, 1)); 793 | for (var key in obj) { 794 | if (!_.contains(keys, key)) copy[key] = obj[key]; 795 | } 796 | return copy; 797 | }; 798 | 799 | // Fill in a given object with default properties. 800 | _.defaults = function(obj) { 801 | each(slice.call(arguments, 1), function(source) { 802 | if (source) { 803 | for (var prop in source) { 804 | if (obj[prop] == null) obj[prop] = source[prop]; 805 | } 806 | } 807 | }); 808 | return obj; 809 | }; 810 | 811 | // Create a (shallow-cloned) duplicate of an object. 812 | _.clone = function(obj) { 813 | if (!_.isObject(obj)) return obj; 814 | return _.isArray(obj) ? obj.slice() : _.extend({}, obj); 815 | }; 816 | 817 | // Invokes interceptor with the obj, and then returns obj. 818 | // The primary purpose of this method is to "tap into" a method chain, in 819 | // order to perform operations on intermediate results within the chain. 820 | _.tap = function(obj, interceptor) { 821 | interceptor(obj); 822 | return obj; 823 | }; 824 | 825 | // Internal recursive comparison function for `isEqual`. 826 | var eq = function(a, b, aStack, bStack) { 827 | // Identical objects are equal. `0 === -0`, but they aren't identical. 828 | // See the Harmony `egal` proposal: http://wiki.ecmascript.org/doku.php?id=harmony:egal. 829 | if (a === b) return a !== 0 || 1 / a == 1 / b; 830 | // A strict comparison is necessary because `null == undefined`. 831 | if (a == null || b == null) return a === b; 832 | // Unwrap any wrapped objects. 833 | if (a instanceof _) a = a._wrapped; 834 | if (b instanceof _) b = b._wrapped; 835 | // Compare `[[Class]]` names. 836 | var className = toString.call(a); 837 | if (className != toString.call(b)) return false; 838 | switch (className) { 839 | // Strings, numbers, dates, and booleans are compared by value. 840 | case '[object String]': 841 | // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is 842 | // equivalent to `new String("5")`. 843 | return a == String(b); 844 | case '[object Number]': 845 | // `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for 846 | // other numeric values. 847 | return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b); 848 | case '[object Date]': 849 | case '[object Boolean]': 850 | // Coerce dates and booleans to numeric primitive values. Dates are compared by their 851 | // millisecond representations. Note that invalid dates with millisecond representations 852 | // of `NaN` are not equivalent. 853 | return +a == +b; 854 | // RegExps are compared by their source patterns and flags. 855 | case '[object RegExp]': 856 | return a.source == b.source && 857 | a.global == b.global && 858 | a.multiline == b.multiline && 859 | a.ignoreCase == b.ignoreCase; 860 | } 861 | if (typeof a != 'object' || typeof b != 'object') return false; 862 | // Assume equality for cyclic structures. The algorithm for detecting cyclic 863 | // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`. 864 | var length = aStack.length; 865 | while (length--) { 866 | // Linear search. Performance is inversely proportional to the number of 867 | // unique nested structures. 868 | if (aStack[length] == a) return bStack[length] == b; 869 | } 870 | // Add the first object to the stack of traversed objects. 871 | aStack.push(a); 872 | bStack.push(b); 873 | var size = 0, result = true; 874 | // Recursively compare objects and arrays. 875 | if (className == '[object Array]') { 876 | // Compare array lengths to determine if a deep comparison is necessary. 877 | size = a.length; 878 | result = size == b.length; 879 | if (result) { 880 | // Deep compare the contents, ignoring non-numeric properties. 881 | while (size--) { 882 | if (!(result = eq(a[size], b[size], aStack, bStack))) break; 883 | } 884 | } 885 | } else { 886 | // Objects with different constructors are not equivalent, but `Object`s 887 | // from different frames are. 888 | var aCtor = a.constructor, bCtor = b.constructor; 889 | if (aCtor !== bCtor && !(_.isFunction(aCtor) && (aCtor instanceof aCtor) && 890 | _.isFunction(bCtor) && (bCtor instanceof bCtor))) { 891 | return false; 892 | } 893 | // Deep compare objects. 894 | for (var key in a) { 895 | if (_.has(a, key)) { 896 | // Count the expected number of properties. 897 | size++; 898 | // Deep compare each member. 899 | if (!(result = _.has(b, key) && eq(a[key], b[key], aStack, bStack))) break; 900 | } 901 | } 902 | // Ensure that both objects contain the same number of properties. 903 | if (result) { 904 | for (key in b) { 905 | if (_.has(b, key) && !(size--)) break; 906 | } 907 | result = !size; 908 | } 909 | } 910 | // Remove the first object from the stack of traversed objects. 911 | aStack.pop(); 912 | bStack.pop(); 913 | return result; 914 | }; 915 | 916 | // Perform a deep comparison to check if two objects are equal. 917 | _.isEqual = function(a, b) { 918 | return eq(a, b, [], []); 919 | }; 920 | 921 | // Is a given array, string, or object empty? 922 | // An "empty" object has no enumerable own-properties. 923 | _.isEmpty = function(obj) { 924 | if (obj == null) return true; 925 | if (_.isArray(obj) || _.isString(obj)) return obj.length === 0; 926 | for (var key in obj) if (_.has(obj, key)) return false; 927 | return true; 928 | }; 929 | 930 | // Is a given value a DOM element? 931 | _.isElement = function(obj) { 932 | return !!(obj && obj.nodeType === 1); 933 | }; 934 | 935 | // Is a given value an array? 936 | // Delegates to ECMA5's native Array.isArray 937 | _.isArray = nativeIsArray || function(obj) { 938 | return toString.call(obj) == '[object Array]'; 939 | }; 940 | 941 | // Is a given variable an object? 942 | _.isObject = function(obj) { 943 | return obj === Object(obj); 944 | }; 945 | 946 | // Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp. 947 | each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp'], function(name) { 948 | _['is' + name] = function(obj) { 949 | return toString.call(obj) == '[object ' + name + ']'; 950 | }; 951 | }); 952 | 953 | // Define a fallback version of the method in browsers (ahem, IE), where 954 | // there isn't any inspectable "Arguments" type. 955 | if (!_.isArguments(arguments)) { 956 | _.isArguments = function(obj) { 957 | return !!(obj && _.has(obj, 'callee')); 958 | }; 959 | } 960 | 961 | // Optimize `isFunction` if appropriate. 962 | if (typeof (/./) !== 'function') { 963 | _.isFunction = function(obj) { 964 | return typeof obj === 'function'; 965 | }; 966 | } 967 | 968 | // Is a given object a finite number? 969 | _.isFinite = function(obj) { 970 | return isFinite(obj) && !isNaN(parseFloat(obj)); 971 | }; 972 | 973 | // Is the given value `NaN`? (NaN is the only number which does not equal itself). 974 | _.isNaN = function(obj) { 975 | return _.isNumber(obj) && obj != +obj; 976 | }; 977 | 978 | // Is a given value a boolean? 979 | _.isBoolean = function(obj) { 980 | return obj === true || obj === false || toString.call(obj) == '[object Boolean]'; 981 | }; 982 | 983 | // Is a given value equal to null? 984 | _.isNull = function(obj) { 985 | return obj === null; 986 | }; 987 | 988 | // Is a given variable undefined? 989 | _.isUndefined = function(obj) { 990 | return obj === void 0; 991 | }; 992 | 993 | // Shortcut function for checking if an object has a given property directly 994 | // on itself (in other words, not on a prototype). 995 | _.has = function(obj, key) { 996 | return hasOwnProperty.call(obj, key); 997 | }; 998 | 999 | // Utility Functions 1000 | // ----------------- 1001 | 1002 | // Run Underscore.js in *noConflict* mode, returning the `_` variable to its 1003 | // previous owner. Returns a reference to the Underscore object. 1004 | _.noConflict = function() { 1005 | root._ = previousUnderscore; 1006 | return this; 1007 | }; 1008 | 1009 | // Keep the identity function around for default iterators. 1010 | _.identity = function(value) { 1011 | return value; 1012 | }; 1013 | 1014 | // Run a function **n** times. 1015 | _.times = function(n, iterator, context) { 1016 | var accum = Array(n); 1017 | for (var i = 0; i < n; i++) accum[i] = iterator.call(context, i); 1018 | return accum; 1019 | }; 1020 | 1021 | // Return a random integer between min and max (inclusive). 1022 | _.random = function(min, max) { 1023 | if (max == null) { 1024 | max = min; 1025 | min = 0; 1026 | } 1027 | return min + Math.floor(Math.random() * (max - min + 1)); 1028 | }; 1029 | 1030 | // List of HTML entities for escaping. 1031 | var entityMap = { 1032 | escape: { 1033 | '&': '&', 1034 | '<': '<', 1035 | '>': '>', 1036 | '"': '"', 1037 | "'": ''', 1038 | '/': '/' 1039 | } 1040 | }; 1041 | entityMap.unescape = _.invert(entityMap.escape); 1042 | 1043 | // Regexes containing the keys and values listed immediately above. 1044 | var entityRegexes = { 1045 | escape: new RegExp('[' + _.keys(entityMap.escape).join('') + ']', 'g'), 1046 | unescape: new RegExp('(' + _.keys(entityMap.unescape).join('|') + ')', 'g') 1047 | }; 1048 | 1049 | // Functions for escaping and unescaping strings to/from HTML interpolation. 1050 | _.each(['escape', 'unescape'], function(method) { 1051 | _[method] = function(string) { 1052 | if (string == null) return ''; 1053 | return ('' + string).replace(entityRegexes[method], function(match) { 1054 | return entityMap[method][match]; 1055 | }); 1056 | }; 1057 | }); 1058 | 1059 | // If the value of the named property is a function then invoke it; 1060 | // otherwise, return it. 1061 | _.result = function(object, property) { 1062 | if (object == null) return null; 1063 | var value = object[property]; 1064 | return _.isFunction(value) ? value.call(object) : value; 1065 | }; 1066 | 1067 | // Add your own custom functions to the Underscore object. 1068 | _.mixin = function(obj) { 1069 | each(_.functions(obj), function(name){ 1070 | var func = _[name] = obj[name]; 1071 | _.prototype[name] = function() { 1072 | var args = [this._wrapped]; 1073 | push.apply(args, arguments); 1074 | return result.call(this, func.apply(_, args)); 1075 | }; 1076 | }); 1077 | }; 1078 | 1079 | // Generate a unique integer id (unique within the entire client session). 1080 | // Useful for temporary DOM ids. 1081 | var idCounter = 0; 1082 | _.uniqueId = function(prefix) { 1083 | var id = ++idCounter + ''; 1084 | return prefix ? prefix + id : id; 1085 | }; 1086 | 1087 | // By default, Underscore uses ERB-style template delimiters, change the 1088 | // following template settings to use alternative delimiters. 1089 | _.templateSettings = { 1090 | evaluate : /<%([\s\S]+?)%>/g, 1091 | interpolate : /<%=([\s\S]+?)%>/g, 1092 | escape : /<%-([\s\S]+?)%>/g 1093 | }; 1094 | 1095 | // When customizing `templateSettings`, if you don't want to define an 1096 | // interpolation, evaluation or escaping regex, we need one that is 1097 | // guaranteed not to match. 1098 | var noMatch = /(.)^/; 1099 | 1100 | // Certain characters need to be escaped so that they can be put into a 1101 | // string literal. 1102 | var escapes = { 1103 | "'": "'", 1104 | '\\': '\\', 1105 | '\r': 'r', 1106 | '\n': 'n', 1107 | '\t': 't', 1108 | '\u2028': 'u2028', 1109 | '\u2029': 'u2029' 1110 | }; 1111 | 1112 | var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g; 1113 | 1114 | // JavaScript micro-templating, similar to John Resig's implementation. 1115 | // Underscore templating handles arbitrary delimiters, preserves whitespace, 1116 | // and correctly escapes quotes within interpolated code. 1117 | _.template = function(text, data, settings) { 1118 | var render; 1119 | settings = _.defaults({}, settings, _.templateSettings); 1120 | 1121 | // Combine delimiters into one regular expression via alternation. 1122 | var matcher = new RegExp([ 1123 | (settings.escape || noMatch).source, 1124 | (settings.interpolate || noMatch).source, 1125 | (settings.evaluate || noMatch).source 1126 | ].join('|') + '|$', 'g'); 1127 | 1128 | // Compile the template source, escaping string literals appropriately. 1129 | var index = 0; 1130 | var source = "__p+='"; 1131 | text.replace(matcher, function(match, escape, interpolate, evaluate, offset) { 1132 | source += text.slice(index, offset) 1133 | .replace(escaper, function(match) { return '\\' + escapes[match]; }); 1134 | 1135 | if (escape) { 1136 | source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'"; 1137 | } 1138 | if (interpolate) { 1139 | source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'"; 1140 | } 1141 | if (evaluate) { 1142 | source += "';\n" + evaluate + "\n__p+='"; 1143 | } 1144 | index = offset + match.length; 1145 | return match; 1146 | }); 1147 | source += "';\n"; 1148 | 1149 | // If a variable is not specified, place data values in local scope. 1150 | if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n'; 1151 | 1152 | source = "var __t,__p='',__j=Array.prototype.join," + 1153 | "print=function(){__p+=__j.call(arguments,'');};\n" + 1154 | source + "return __p;\n"; 1155 | 1156 | try { 1157 | render = new Function(settings.variable || 'obj', '_', source); 1158 | } catch (e) { 1159 | e.source = source; 1160 | throw e; 1161 | } 1162 | 1163 | if (data) return render(data, _); 1164 | var template = function(data) { 1165 | return render.call(this, data, _); 1166 | }; 1167 | 1168 | // Provide the compiled function source as a convenience for precompilation. 1169 | template.source = 'function(' + (settings.variable || 'obj') + '){\n' + source + '}'; 1170 | 1171 | return template; 1172 | }; 1173 | 1174 | // Add a "chain" function, which will delegate to the wrapper. 1175 | _.chain = function(obj) { 1176 | return _(obj).chain(); 1177 | }; 1178 | 1179 | // OOP 1180 | // --------------- 1181 | // If Underscore is called as a function, it returns a wrapped object that 1182 | // can be used OO-style. This wrapper holds altered versions of all the 1183 | // underscore functions. Wrapped objects may be chained. 1184 | 1185 | // Helper function to continue chaining intermediate results. 1186 | var result = function(obj) { 1187 | return this._chain ? _(obj).chain() : obj; 1188 | }; 1189 | 1190 | // Add all of the Underscore functions to the wrapper object. 1191 | _.mixin(_); 1192 | 1193 | // Add all mutator Array functions to the wrapper. 1194 | each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) { 1195 | var method = ArrayProto[name]; 1196 | _.prototype[name] = function() { 1197 | var obj = this._wrapped; 1198 | method.apply(obj, arguments); 1199 | if ((name == 'shift' || name == 'splice') && obj.length === 0) delete obj[0]; 1200 | return result.call(this, obj); 1201 | }; 1202 | }); 1203 | 1204 | // Add all accessor Array functions to the wrapper. 1205 | each(['concat', 'join', 'slice'], function(name) { 1206 | var method = ArrayProto[name]; 1207 | _.prototype[name] = function() { 1208 | return result.call(this, method.apply(this._wrapped, arguments)); 1209 | }; 1210 | }); 1211 | 1212 | _.extend(_.prototype, { 1213 | 1214 | // Start chaining a wrapped Underscore object. 1215 | chain: function() { 1216 | this._chain = true; 1217 | return this; 1218 | }, 1219 | 1220 | // Extracts the result from a wrapped and chained object. 1221 | value: function() { 1222 | return this._wrapped; 1223 | } 1224 | 1225 | }); 1226 | 1227 | }).call(this); 1228 | -------------------------------------------------------------------------------- /spec/bloomFilterSpec.js: -------------------------------------------------------------------------------- 1 | var generateString = function() { 2 | var s = ''; 3 | for (var i = 0; i < 6; i++) { 4 | s = s + String.fromCharCode(Math.random() * 10000); 5 | } 6 | return s; 7 | }; 8 | 9 | describe('bloomFilter', function() { 10 | var bloomFilter; 11 | 12 | beforeEach(function() { 13 | bloomFilter = new BloomFilter(18, hashingFunctions); 14 | }); 15 | 16 | it('Should have methods "test" and "add"' , function() { 17 | expect(bloomFilter.test).to.be.a("function"); 18 | expect(bloomFilter.add).to.be.a("function"); 19 | }); 20 | 21 | it('Should have property _storage that is an instance of BitArray', function() { 22 | expect(bloomFilter._storage instanceof BitArray).to.equal(true); 23 | }); 24 | 25 | it('Should return true for any values in the filter', function() { 26 | bloomFilter.add('Hi'); 27 | bloomFilter.add('You'); 28 | bloomFilter.add('Crazy'); 29 | bloomFilter.add('Diamond!'); 30 | expect(bloomFilter.test('Hi')).to.equal(true); 31 | expect(bloomFilter.test('You')).to.equal(true); 32 | expect(bloomFilter.test('Crazy')).to.equal(true); 33 | expect(bloomFilter.test('Diamond!')).to.equal(true); 34 | }); 35 | 36 | it('Should return false (most of the time) for values not in the filter', function() { 37 | bloomFilter.add("What's Up?"); 38 | bloomFilter.add('Coding is rad!'); 39 | bloomFilter.add('What, is this a center for ants?!?'); 40 | bloomFilter.add("IT'S 1:30AM WHAT AM I DOING?!?!?!?"); 41 | var positives = 0; 42 | for (var i = 0; i < 10001; i++) { 43 | if (bloomFilter.test(generateString())) { 44 | positives++; 45 | } 46 | } 47 | expect((positives / 100000) < 0.4).to.equal(true); 48 | }); 49 | 50 | it('Should have a failure rate that is approximately the known failure rate', function() { 51 | bloomFilter.add('Hey there'); 52 | bloomFilter.add('Do not be scared'); 53 | bloomFilter.add('Everything is fine'); 54 | var estFailureRate = Math.pow(1- (Math.exp(-0.5)), 3); 55 | var positives = 0; 56 | for (var i = 0; i < 10001; i++) { 57 | if (bloomFilter.test(generateString())) { 58 | positives++; 59 | } 60 | } 61 | var actualFailureRate = (positives / 10000); 62 | expect((Math.abs(actualFailureRate - estFailureRate)) < 0.2).to.equal(true); 63 | }); 64 | }); 65 | -------------------------------------------------------------------------------- /src/bitArray.js: -------------------------------------------------------------------------------- 1 | /* 2 | Author: Adam Van Antwerp 3 | Date Created: 3/31/2015 4 | Last Modification: 4/9/2015 5 | Description: This implements a simple BitArray class for doing bitwise 6 | storing and manipulation. 7 | */ 8 | 9 | var BitArray = function(length) { 10 | //The length of the desired bit array. 11 | this.length = length; 12 | 13 | //This is the internal unsigned 8 bit integer array that will serve as the 14 | //bit array. 15 | this._uint8 = new Uint8Array(Math.ceil(this.length / 8)); 16 | }; 17 | 18 | BitArray.prototype.calculateIndex = function(idx) { 19 | //To determine the desired position to operate on the unsigned 8 bit int 20 | //array, divide the given index by 8 and floor the result. 21 | return Math.floor(idx / 8); 22 | }; 23 | 24 | BitArray.prototype.generateMask = function(idx) { 25 | //The mask is used to turn on or check the desired bit in a position's 26 | //8 bit integer. We determine the mask integer by finding the remainder 27 | //of the desired index divided by 8 and calculating 2 to that number. 28 | return Math.pow(2, (idx % 8)); 29 | }; 30 | 31 | BitArray.prototype.trueBits = function(integer) { 32 | //This method takes an unsigned integer and efficiently calculates the number 33 | //of true bits that integer is represented by in binary. 34 | 35 | //Start the running total at 0 36 | var total = 0; 37 | 38 | //The number of 1 bits is equal to the number of times you can take an integer 39 | //and bitwise AND it with that number minus 1. We will iterate until the 40 | //number is equal to 0. 41 | while (integer !== 0) { 42 | integer = integer & (integer - 1); 43 | total++; 44 | } 45 | 46 | return total; 47 | }; 48 | 49 | 50 | BitArray.prototype.flipOn = function(idx) { 51 | //Use calculateIndex to determine the desired bitwise index. 52 | var index = this.calculateIndex(idx); 53 | 54 | //Use generateMask to get the desired mask for bitwise manipulation. 55 | var mask = this.generateMask(idx); 56 | 57 | //Now, the magic happens. To 'turn on' the desired bit, we do a bitwise 58 | //OR against the current 8 bit integer in the calculated index. 59 | this._uint8[index] = this._uint8[index] | mask; 60 | }; 61 | 62 | BitArray.prototype.flipOff = function(idx) { 63 | //Use calculateIndex to determine the desired bitwise index. 64 | var index = this.calculateIndex(idx); 65 | 66 | //Use generate mask, then subtract it from 255 to make a mask to flip 67 | //off the bit. 68 | var mask = 255 - this.generateMask(idx); 69 | 70 | //Now and the current integer with the generated mask to flipp off the bit. 71 | this._uint8[index] = this._uint8[index] & mask; 72 | }; 73 | 74 | BitArray.prototype.toggle = function(idx) { 75 | //Use calculateIndex to determine the desired bitwise index. 76 | var index = this.calculateIndex(idx); 77 | 78 | //Use generateMask to get the desired mask for bitwise manipulation. 79 | var mask = this.generateMask(idx); 80 | 81 | //To toggle the desired bit, we do a bitwise XOR against the integer with 82 | //the mask. 83 | this._uint8[index] = this._uint8[index] ^ mask; 84 | }; 85 | 86 | BitArray.prototype.check = function(idx) { 87 | //We need to know the 'bitwise' index we would like to check, so we call 88 | //calculateIndex. 89 | var index = this.calculateIndex(idx); 90 | 91 | //Just like before, we need to generate the desired mask in order to check 92 | //if a peticular bit is on. 93 | var mask = this.generateMask(idx); 94 | 95 | //To check if a certain bit is 'on', we simply bitwise AND the mask against 96 | //the desired 8 bit integer. If it returns something other than 0, than we 97 | //know that that integer is on. 98 | return ((this._uint8[index] & mask) !== 0); 99 | }; 100 | 101 | BitArray.prototype.percentTrue = function() { 102 | //This function is used to determine how much of the array is occupied by 103 | //true (aka 1) bits. 104 | 105 | //Instantiate an integer to keep track of the running total of true bits. 106 | var total = 0; 107 | 108 | //For each element in the array, call trueBits on the unsigned 8 bit integer 109 | //and add that to the running total of true values. 110 | for (var i = 0; i < this._uint8.length; i++) { 111 | total = total + this.trueBits(this._uint8[i]); 112 | } 113 | 114 | return total / this.length; 115 | }; 116 | -------------------------------------------------------------------------------- /src/bloomFilter.js: -------------------------------------------------------------------------------- 1 | // The BloomFilter contstructor expects 2 things; m, which is the number of 2 | // positions in the filter data set, and an array of hashing functions. 3 | var BloomFilter = function(m, hashingFunctions) { 4 | // The bit array object that holds the results of the bloom filter hashing 5 | // should go under the property _storage. 6 | this._storage; 7 | 8 | // Don't forget to grab the parameters from the constructor call and assign 9 | // them! 10 | }; 11 | 12 | // Your methods go here! Good luck! 13 | -------------------------------------------------------------------------------- /src/hashFunctions.js: -------------------------------------------------------------------------------- 1 | var hashingFunction1 = function(range, value){ 2 | var hash = 0; 3 | for (var i = 0; i < value.length; i++) { 4 | hash = (hash<<5) + hash + value.charCodeAt(i); 5 | hash = hash & hash; // Convert to 32bit integer 6 | hash = Math.abs(hash); 7 | } 8 | return hash % range; 9 | }; 10 | 11 | var hashingFunction2 = function(range, value){ 12 | var result = value.split(''); 13 | 14 | result = (result.map(function(value) { 15 | return value.charCodeAt(0); 16 | }).map(function(value) { 17 | return (value ^ range); 18 | }).reduce(function(a, b) { 19 | return a + b ; 20 | })) ^ 255; 21 | return result % range; 22 | }; 23 | 24 | var hashingFunction3 = function(range, value){ 25 | return (value.length ^ 255) % range; 26 | }; 27 | 28 | var hashingFunctions = [hashingFunction1, hashingFunction2, hashingFunction3]; 29 | --------------------------------------------------------------------------------