├── 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 | 
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 | 
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 | 
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 |
--------------------------------------------------------------------------------