├── .gitignore
├── LICENSE
├── README.md
├── css
└── demo.css
├── index.html
└── js
├── demo.js
├── demo
└── demo-app.js
└── lib
├── require.js
└── trie
├── base64.js
├── bitstring.js
├── bitwriter.js
├── compressedtrie.js
├── compressedtrienode.js
├── rankdirectory.js
├── simpleselectindex.js
├── stringintarray.js
└── trie.js
/.gitignore:
--------------------------------------------------------------------------------
1 | *~
2 | *,*~
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (C) 2013, Tim Niblett
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining
4 | a copy of this software and associated documentation files (the
5 | "Software"), to deal in the Software without restriction, including
6 | without limitation the rights to use, copy, modify, merge, publish,
7 | distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so, subject to
9 | the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be
12 | included in all copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Client-side pure JavaScript Trie
2 |
3 | A pure javascript client-side trie-based dictionary, which is
4 | compact and can do fast look-ups.
5 |
6 | The library is pretty-much a direct copy of code from [Steve
7 | Hanov](http://stevehanov.ca/blog/index.php?id=120).
8 |
9 | The changes I've made are (a) set up for `RequireJS`, and (b) added the
10 | `SimpleSelectIndex` class to speed up word lookup. This was needed for a
11 | a [Boggle Solver](http://solveboggle.appspot.com) I made.
12 |
13 | You can try the code out by downloading it and loading
14 | `index.html` into a browser. A demo is available [here](http://cilogi.github.io/jstrie/index.html)
15 |
16 | ## Motivation
17 |
18 | The original motivation was to solve Boggle client-side, with the
19 | following requirements:
20 |
21 | * the amount of data uploaded should be small, for a fast start-up;
22 | * the time for code to initialize to be fast, again to enable a fast start-up;
23 | * and the run-time performance should be "fast enough" so that users
24 | aren't aware of waiting around for results.
25 |
26 | These are non-trivial requirements to implement in a webapp such as
27 | Boggle where a large dictionary is needed and a significant amount of
28 | computation takes place to "solve" a setup. The upside is that, if
29 | these requirements _can_ be met, a large number of users can be
30 | supported at low cost as all the computation is done
31 | client-side. The other advantage of a client-side solution is that it
32 | can work offline, since nothing is required from the server in routine
33 | operation.
34 |
35 | There are, of course, other applications of a large but compact
36 | dictionary, with fast lookup, stored client-side.
37 |
38 | ### Succinct Tries
39 |
40 | John Resig wrote a series of blog posts ([here][1], [here][2] and
41 | [here][3]) a couple of years ago, studying a similar problem --
42 | looking up words from a dictionary.
43 |
44 | An interesting solution Resig looked at was supplied by
45 | [Steve Hanov][4] who used a succinct data structure to implement a
46 | [Trie][15].
47 |
48 | Succinct data structures were introduced by Jacobson (G. J. Jacobson 1988)
49 | in his PhD thesis (a summary paper can be found in
50 | [(G. Jacobson 1989))][16]. They have interesting properties, being both space
51 | and time efficient. Succinct trees are asymptotically space-optimal
52 | yet can be used directly, without decompression or other
53 | pre-processing, and perform within a small constant time factor of
54 | pointer-based data structures.
55 |
56 | This is very useful for our problem: the data structures can be sent
57 | from the server without taking up too much space, and the client does
58 | not need to decode the structures in order to process them. This is
59 | important for JavaScript as [Resig found][2] that decompressing a trie
60 | can take a long time on mobile devices.
61 |
62 | Hanov's solution, for which he provides [JavaScript][5] code,
63 | implements Jacobson's ideas with a twist. Bitstrings are stored in
64 | [Base64][6], which means the data doesn't need to be decoded
65 | client-side, at the expense of storing 6 bits of information in an
66 | 8-bit byte.
67 |
68 | The only problem with Hanov's solution is that it is slow. I
69 | downloaded a word list containing about `270,000` words from
70 | [this file][8] [here][7] and found that using Hanov's trie Boggle took
71 | about 20 seconds to solve on the HTC Desire, which is not acceptable.
72 |
73 | For John Resig's problem, looking up a single word, Hanov's solution
74 | is probably OK as only a few nodes of the Trie have to be searched.
75 | The Boggle solver has to look at 3-5000 nodes of the trie as we're
76 | doing an exhaustive search of the Boggle board and this is too slow by
77 | at least an order of magnitude if we are to meet our run-time
78 | performance requirements.
79 |
80 | The bottleneck occurs with the algorithm for the `select`
81 | function. `select(i)` finds the location in a bit string of the `i`th
82 | occurrence of the zero bit. Hanov's implementation is based on the
83 | `rank` function where `rank(i)` is the number of zero bits set up to
84 | and including position `i`. These two functions are inverses, so that
85 | if `rank(j) = k` then `select(k) = j`. Hanov's algorithm does a
86 | binary search over the `rank` function to determine select, and this
87 | is slow.
88 |
89 | I have implemented a simple alternative `select` function which stores
90 | an array `S` where the `i`th element contains the value for
91 | `select(i * N)` where `N` is set at `32`, but can be any number. I've
92 | tried `64` which uses half the space and takes about 30% longer. To
93 | find `select(k)` we find `S[k/N]` and can then count the remaining
94 | bits. In effect each select is then looking for a value less than
95 | `N`. Performance depends on the distribution of the zeros and ones
96 | being reasonably uniform (which it is in this case). The speedup is
97 | significant -- in my tests a factor of about `14`. This makes the
98 | wait for a solution about a second on a slow phone, which is
99 | acceptable.
100 |
101 | Having implemented this solution it looks very similar to that
102 | proposed, implemented and tested in [(Navarro and Providel 2012)][17]
103 | for `select` queries.
104 |
105 | ----
106 |
107 | So, we end up with a data structure, which is reasonably compact. The
108 | gzipped trie (ie the size sent over the wire) is `368,640` bytes,
109 | while the gzipped dictionary is `470,298` bytes. The uncompressed
110 | dictionary is _much_ larger than the uncompressed trie of course. So using the
111 | succinct structure saves space compared to the raw dictionary as well
112 | as being optimised for word search, and we can do everything in
113 | JavaScript with reasonable performance.
114 |
115 | ### Offline HTML
116 |
117 | Setting up our web site/app to work offline is straightforward if we
118 | use the [Offline cache][9] feature of HTML5 (well described
119 | [here][10]).
120 |
121 | ## References
122 |
123 | Jacobson, Guy Joseph. 1988. “Succinct static data structures.”
124 | Pittsburgh, PA, USA.
125 |
126 | [Jacobson, Guy. 1989.][16] “Space-efficient static trees and graphs.” In
127 | Foundations of Computer Science, 1989., 30th Annual Symposium on,
128 | 549–554. IEEE.
129 |
130 | [Navarro, Gonzalo, and Eliana Providel. 2012.][17] “Fast, small, simple
131 | rank/select on bitmaps.” In Experimental Algorithms,
132 | 295–306. Springer.
133 |
134 | [1]: http://ejohn.org/blog/dictionary-lookups-in-javascript/
135 | [2]: http://ejohn.org/blog/javascript-trie-performance-analysis/
136 | [3]: http://ejohn.org/blog/revised-javascript-dictionary-search/
137 | [4]: http://stevehanov.ca/blog/index.php?id=120
138 | [5]: http://www.hanovsolutions.com/trie/Bits.js
139 | [6]: http://en.wikipedia.org/wiki/Base64
140 | [7]: http://www.isc.ro/en/commands/lists.html
141 | [8]: http://www.isc.ro/lists/twl06.zip
142 | [9]: http://www.whatwg.org/specs/web-apps/current-work/multipage/offline.html
143 | [10]: http://diveintohtml5.info/offline.html
144 | [11]: http://en.wiktionary.org/
145 | [12]: http://www.wordnik.com/
146 | [13]: http://solveboggle.appspot.com/service
147 | [14]: http://timniblett.github.com/solveboggle
148 | [15]: http://en.wikipedia.org/wiki/Trie
149 | [16]: http://www.cs.cmu.edu/afs/cs/project/aladdin/wwwlocal/compression/00063533.pdf
150 | [17]: http://www.dcc.uchile.cl/~gnavarro/ps/sea12.1.pdf
151 |
152 |
153 |
--------------------------------------------------------------------------------
/css/demo.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-family: sans-serif;
3 | color: #777;
4 | }
5 |
6 |
7 | /* Add transition to containers so they can push in and out */
8 | #layout {
9 | -webkit-transition: all 0.2s ease-out;
10 | -moz-transition: all 0.2s ease-out;
11 | -ms-transition: all 0.2s ease-out;
12 | -o-transition: all 0.2s ease-out;
13 | transition: all 0.2s ease-out;
14 | }
15 |
16 |
17 | /* --------------------------
18 | * Element Styles
19 | * --------------------------
20 | */
21 |
22 |
23 | h1, h2, h3, h4, h5, h6 {
24 | font-weight: bold;
25 | color: rgb(75, 75, 75);
26 | }
27 |
28 | #layout {
29 | padding-left: 150px; /* left col width "#menu" */
30 | left: 0;
31 | }
32 |
33 | .header {
34 | min-height: 80px;
35 | margin: 0;
36 | color: #333;
37 | padding: 1em;
38 | text-align: center;
39 | border-bottom: 1px solid #eee;
40 | background: #fff;
41 | }
42 | .header h1 {
43 | margin: 0.2em 0;
44 | font-size: 3em;
45 | font-weight: 300;
46 | }
47 | .header h2 {
48 | font-weight: 300;
49 | margin: 0;
50 | color: #ccc;
51 | }
52 |
53 |
54 | .content {
55 | margin: 0 auto;
56 | width: 70%;
57 | margin-bottom:50px;
58 | }
59 | .content p {
60 | line-height: 1.6em;
61 | font-size: 1.125em;
62 | }
63 |
64 |
65 | .pure-button {
66 | font-family: inherit;
67 | }
68 |
69 |
70 | @media (max-width: 870px) {
71 |
72 | /* Navigation Push Styles */
73 | #layout {
74 | position: relative;
75 | padding-left: 0;
76 | }
77 |
78 | }
79 |
80 | @media (max-width: 767px) {
81 |
82 | .content {
83 | width: 95%;
84 | font-size: 90%;
85 | }
86 | }
87 |
88 | p {
89 | line-height: 1.6em;
90 | }
91 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Offline Boggle Solver
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
22 |
23 |
24 |
25 | Type or paste in words separated by spaces and/or newlines
26 |
27 |
28 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/js/demo.js:
--------------------------------------------------------------------------------
1 |
2 | require.config({
3 | baseUrl: "js",
4 | catchError : {
5 | define: true
6 | },
7 | // 3rd party script alias names
8 | paths: {
9 | "jquery" : "http://code.jquery.com/jquery-1.10.1.min",
10 | "trie" : "lib/trie"
11 | }
12 |
13 |
14 | });
15 |
16 |
17 | require(['jquery', 'demo/demo-app'], function ($, demoApp) {
18 |
19 | // What to do on button presses
20 | $(document).ready(function() {
21 | $("#create").click(function(e) {
22 | var val = $("#dict").val();
23 | demoApp.loadDict(val.toLowerCase());
24 | $("#trie").val(demoApp.compressedTrie());
25 | $("#lookup-button").removeAttr('disabled');
26 | $("#result").html("");
27 | e.preventDefault();
28 | return false;
29 | });
30 |
31 | $("#lookup-button").click(function(e) {
32 | var isIn, word = $("#lookup").val().trim().toLowerCase();
33 | if (word && word.length) {
34 | isIn = demoApp.lookup(word);
35 | $("#result").html(word + (isIn ? " is in the dictionary" : " is not in the dictionary"));
36 | }
37 | e.preventDefault();
38 | return false;
39 | });
40 | });
41 |
42 | });
43 |
--------------------------------------------------------------------------------
/js/demo/demo-app.js:
--------------------------------------------------------------------------------
1 | define(['jquery', "trie/trie", "trie/compressedtrie"], function($, Trie, CompressedTrie) {
2 |
3 | var trie = null, compressedTrie = null;
4 |
5 | function loadDict(inputString) {
6 | var i,
7 | words = inputString.split(/\s+/).sort();
8 |
9 | trie = new Trie();
10 | for (i = 0; i < words.length; i++) {
11 | if (words[i].match(/^[a-z]+$/)) {
12 | trie.insert(words[i]);
13 | }
14 | }
15 | compressedTrie = compress();
16 | }
17 |
18 | function compress() {
19 | var nodeCount = trie.getNodeCount(),
20 | trieData = trie.encode();
21 |
22 | return new CompressedTrie(trieData, nodeCount);
23 | }
24 |
25 | function lookup(word) {
26 | return compressedTrie.lookup(word);
27 | }
28 |
29 | return {
30 | loadDict: loadDict,
31 | compressedTrie : function() { return JSON.stringify(compressedTrie.toJSON(), null, "\t"); },
32 | lookup: lookup
33 | }
34 | })
--------------------------------------------------------------------------------
/js/lib/require.js:
--------------------------------------------------------------------------------
1 | /** vim: et:ts=4:sw=4:sts=4
2 | * @license RequireJS 2.1.1 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, jQuery, 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.1',
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 | aps = ap.slice,
25 | apsp = ap.splice,
26 | isBrowser = !!(typeof window !== 'undefined' && navigator && document),
27 | isWebWorker = !isBrowser && typeof importScripts !== 'undefined',
28 | //PS3 indicates loaded and complete, but need to wait for complete
29 | //specifically. Sequence is 'loading', 'loaded', execution,
30 | // then 'complete'. The UA check is unfortunate, but not sure how
31 | //to feature test w/o causing perf issues.
32 | readyRegExp = isBrowser && navigator.platform === 'PLAYSTATION 3' ?
33 | /^complete$/ : /^(complete|loaded)$/,
34 | defContextName = '_',
35 | //Oh the tragedy, detecting opera. See the usage of isOpera for reason.
36 | isOpera = typeof opera !== 'undefined' && opera.toString() === '[object Opera]',
37 | contexts = {},
38 | cfg = {},
39 | globalDefQueue = [],
40 | useInteractive = false;
41 |
42 | function isFunction(it) {
43 | return ostring.call(it) === '[object Function]';
44 | }
45 |
46 | function isArray(it) {
47 | return ostring.call(it) === '[object Array]';
48 | }
49 |
50 | /**
51 | * Helper function for iterating over an array. If the func returns
52 | * a true value, it will break out of the loop.
53 | */
54 | function each(ary, func) {
55 | if (ary) {
56 | var i;
57 | for (i = 0; i < ary.length; i += 1) {
58 | if (ary[i] && func(ary[i], i, ary)) {
59 | break;
60 | }
61 | }
62 | }
63 | }
64 |
65 | /**
66 | * Helper function for iterating over an array backwards. If the func
67 | * returns a true value, it will break out of the loop.
68 | */
69 | function eachReverse(ary, func) {
70 | if (ary) {
71 | var i;
72 | for (i = ary.length - 1; i > -1; i -= 1) {
73 | if (ary[i] && func(ary[i], i, ary)) {
74 | break;
75 | }
76 | }
77 | }
78 | }
79 |
80 | function hasProp(obj, prop) {
81 | return hasOwn.call(obj, prop);
82 | }
83 |
84 | /**
85 | * Cycles over properties in an object and calls a function for each
86 | * property value. If the function returns a truthy value, then the
87 | * iteration is stopped.
88 | */
89 | function eachProp(obj, func) {
90 | var prop;
91 | for (prop in obj) {
92 | if (obj.hasOwnProperty(prop)) {
93 | if (func(obj[prop], prop)) {
94 | break;
95 | }
96 | }
97 | }
98 | }
99 |
100 | /**
101 | * Simple function to mix in properties from source into target,
102 | * but only if target does not already have a property of the same name.
103 | */
104 | function mixin(target, source, force, deepStringMixin) {
105 | if (source) {
106 | eachProp(source, function (value, prop) {
107 | if (force || !hasProp(target, prop)) {
108 | if (deepStringMixin && typeof value !== 'string') {
109 | if (!target[prop]) {
110 | target[prop] = {};
111 | }
112 | mixin(target[prop], value, force, deepStringMixin);
113 | } else {
114 | target[prop] = value;
115 | }
116 | }
117 | });
118 | }
119 | return target;
120 | }
121 |
122 | //Similar to Function.prototype.bind, but the 'this' object is specified
123 | //first, since it is easier to read/figure out what 'this' will be.
124 | function bind(obj, fn) {
125 | return function () {
126 | return fn.apply(obj, arguments);
127 | };
128 | }
129 |
130 | function scripts() {
131 | return document.getElementsByTagName('script');
132 | }
133 |
134 | //Allow getting a global that expressed in
135 | //dot notation, like 'a.b.c'.
136 | function getGlobal(value) {
137 | if (!value) {
138 | return value;
139 | }
140 | var g = global;
141 | each(value.split('.'), function (part) {
142 | g = g[part];
143 | });
144 | return g;
145 | }
146 |
147 | /**
148 | * Constructs an error with a pointer to an URL with more information.
149 | * @param {String} id the error ID that maps to an ID on a web page.
150 | * @param {String} message human readable error.
151 | * @param {Error} [err] the original error, if there is one.
152 | *
153 | * @returns {Error}
154 | */
155 | function makeError(id, msg, err, requireModules) {
156 | var e = new Error(msg + '\nhttp://requirejs.org/docs/errors.html#' + id);
157 | e.requireType = id;
158 | e.requireModules = requireModules;
159 | if (err) {
160 | e.originalError = err;
161 | }
162 | return e;
163 | }
164 |
165 | if (typeof define !== 'undefined') {
166 | //If a define is already in play via another AMD loader,
167 | //do not overwrite.
168 | return;
169 | }
170 |
171 | if (typeof requirejs !== 'undefined') {
172 | if (isFunction(requirejs)) {
173 | //Do not overwrite and existing requirejs instance.
174 | return;
175 | }
176 | cfg = requirejs;
177 | requirejs = undefined;
178 | }
179 |
180 | //Allow for a require config object
181 | if (typeof require !== 'undefined' && !isFunction(require)) {
182 | //assume it is a config object.
183 | cfg = require;
184 | require = undefined;
185 | }
186 |
187 | function newContext(contextName) {
188 | var inCheckLoaded, Module, context, handlers,
189 | checkLoadedTimeoutId,
190 | config = {
191 | waitSeconds: 7,
192 | baseUrl: './',
193 | paths: {},
194 | pkgs: {},
195 | shim: {},
196 | map: {},
197 | config: {}
198 | },
199 | registry = {},
200 | undefEvents = {},
201 | defQueue = [],
202 | defined = {},
203 | urlFetched = {},
204 | requireCounter = 1,
205 | unnormalizedCounter = 1;
206 |
207 | /**
208 | * Trims the . and .. from an array of path segments.
209 | * It will keep a leading path segment if a .. will become
210 | * the first path segment, to help with module name lookups,
211 | * which act like paths, but can be remapped. But the end result,
212 | * all paths that use this function should look normalized.
213 | * NOTE: this method MODIFIES the input array.
214 | * @param {Array} ary the array of path segments.
215 | */
216 | function trimDots(ary) {
217 | var i, part;
218 | for (i = 0; ary[i]; i += 1) {
219 | part = ary[i];
220 | if (part === '.') {
221 | ary.splice(i, 1);
222 | i -= 1;
223 | } else if (part === '..') {
224 | if (i === 1 && (ary[2] === '..' || ary[0] === '..')) {
225 | //End of the line. Keep at least one non-dot
226 | //path segment at the front so it can be mapped
227 | //correctly to disk. Otherwise, there is likely
228 | //no path mapping for a path starting with '..'.
229 | //This can still fail, but catches the most reasonable
230 | //uses of ..
231 | break;
232 | } else if (i > 0) {
233 | ary.splice(i - 1, 2);
234 | i -= 2;
235 | }
236 | }
237 | }
238 | }
239 |
240 | /**
241 | * Given a relative module name, like ./something, normalize it to
242 | * a real name that can be mapped to a path.
243 | * @param {String} name the relative name
244 | * @param {String} baseName a real name that the name arg is relative
245 | * to.
246 | * @param {Boolean} applyMap apply the map config to the value. Should
247 | * only be done if this normalization is for a dependency ID.
248 | * @returns {String} normalized name
249 | */
250 | function normalize(name, baseName, applyMap) {
251 | var pkgName, pkgConfig, mapValue, nameParts, i, j, nameSegment,
252 | foundMap, foundI, foundStarMap, starI,
253 | baseParts = baseName && baseName.split('/'),
254 | normalizedBaseParts = baseParts,
255 | map = config.map,
256 | starMap = map && map['*'];
257 |
258 | //Adjust any relative paths.
259 | if (name && name.charAt(0) === '.') {
260 | //If have a base name, try to normalize against it,
261 | //otherwise, assume it is a top-level require that will
262 | //be relative to baseUrl in the end.
263 | if (baseName) {
264 | if (config.pkgs[baseName]) {
265 | //If the baseName is a package name, then just treat it as one
266 | //name to concat the name with.
267 | normalizedBaseParts = baseParts = [baseName];
268 | } else {
269 | //Convert baseName to array, and lop off the last part,
270 | //so that . matches that 'directory' and not name of the baseName's
271 | //module. For instance, baseName of 'one/two/three', maps to
272 | //'one/two/three.js', but we want the directory, 'one/two' for
273 | //this normalization.
274 | normalizedBaseParts = baseParts.slice(0, baseParts.length - 1);
275 | }
276 |
277 | name = normalizedBaseParts.concat(name.split('/'));
278 | trimDots(name);
279 |
280 | //Some use of packages may use a . path to reference the
281 | //'main' module name, so normalize for that.
282 | pkgConfig = config.pkgs[(pkgName = name[0])];
283 | name = name.join('/');
284 | if (pkgConfig && name === pkgName + '/' + pkgConfig.main) {
285 | name = pkgName;
286 | }
287 | } else if (name.indexOf('./') === 0) {
288 | // No baseName, so this is ID is resolved relative
289 | // to baseUrl, pull off the leading dot.
290 | name = name.substring(2);
291 | }
292 | }
293 |
294 | //Apply map config if available.
295 | if (applyMap && (baseParts || starMap) && map) {
296 | nameParts = name.split('/');
297 |
298 | for (i = nameParts.length; i > 0; i -= 1) {
299 | nameSegment = nameParts.slice(0, i).join('/');
300 |
301 | if (baseParts) {
302 | //Find the longest baseName segment match in the config.
303 | //So, do joins on the biggest to smallest lengths of baseParts.
304 | for (j = baseParts.length; j > 0; j -= 1) {
305 | mapValue = map[baseParts.slice(0, j).join('/')];
306 |
307 | //baseName segment has config, find if it has one for
308 | //this name.
309 | if (mapValue) {
310 | mapValue = mapValue[nameSegment];
311 | if (mapValue) {
312 | //Match, update name to the new value.
313 | foundMap = mapValue;
314 | foundI = i;
315 | break;
316 | }
317 | }
318 | }
319 | }
320 |
321 | if (foundMap) {
322 | break;
323 | }
324 |
325 | //Check for a star map match, but just hold on to it,
326 | //if there is a shorter segment match later in a matching
327 | //config, then favor over this star map.
328 | if (!foundStarMap && starMap && starMap[nameSegment]) {
329 | foundStarMap = starMap[nameSegment];
330 | starI = i;
331 | }
332 | }
333 |
334 | if (!foundMap && foundStarMap) {
335 | foundMap = foundStarMap;
336 | foundI = starI;
337 | }
338 |
339 | if (foundMap) {
340 | nameParts.splice(0, foundI, foundMap);
341 | name = nameParts.join('/');
342 | }
343 | }
344 |
345 | return name;
346 | }
347 |
348 | function removeScript(name) {
349 | if (isBrowser) {
350 | each(scripts(), function (scriptNode) {
351 | if (scriptNode.getAttribute('data-requiremodule') === name &&
352 | scriptNode.getAttribute('data-requirecontext') === context.contextName) {
353 | scriptNode.parentNode.removeChild(scriptNode);
354 | return true;
355 | }
356 | });
357 | }
358 | }
359 |
360 | function hasPathFallback(id) {
361 | var pathConfig = config.paths[id];
362 | if (pathConfig && isArray(pathConfig) && pathConfig.length > 1) {
363 | removeScript(id);
364 | //Pop off the first array value, since it failed, and
365 | //retry
366 | pathConfig.shift();
367 | context.require.undef(id);
368 | context.require([id]);
369 | return true;
370 | }
371 | }
372 |
373 | //Turns a plugin!resource to [plugin, resource]
374 | //with the plugin being undefined if the name
375 | //did not have a plugin prefix.
376 | function splitPrefix(name) {
377 | var prefix,
378 | index = name ? name.indexOf('!') : -1;
379 | if (index > -1) {
380 | prefix = name.substring(0, index);
381 | name = name.substring(index + 1, name.length);
382 | }
383 | return [prefix, name];
384 | }
385 |
386 | /**
387 | * Creates a module mapping that includes plugin prefix, module
388 | * name, and path. If parentModuleMap is provided it will
389 | * also normalize the name via require.normalize()
390 | *
391 | * @param {String} name the module name
392 | * @param {String} [parentModuleMap] parent module map
393 | * for the module name, used to resolve relative names.
394 | * @param {Boolean} isNormalized: is the ID already normalized.
395 | * This is true if this call is done for a define() module ID.
396 | * @param {Boolean} applyMap: apply the map config to the ID.
397 | * Should only be true if this map is for a dependency.
398 | *
399 | * @returns {Object}
400 | */
401 | function makeModuleMap(name, parentModuleMap, isNormalized, applyMap) {
402 | var url, pluginModule, suffix, nameParts,
403 | prefix = null,
404 | parentName = parentModuleMap ? parentModuleMap.name : null,
405 | originalName = name,
406 | isDefine = true,
407 | normalizedName = '';
408 |
409 | //If no name, then it means it is a require call, generate an
410 | //internal name.
411 | if (!name) {
412 | isDefine = false;
413 | name = '_@r' + (requireCounter += 1);
414 | }
415 |
416 | nameParts = splitPrefix(name);
417 | prefix = nameParts[0];
418 | name = nameParts[1];
419 |
420 | if (prefix) {
421 | prefix = normalize(prefix, parentName, applyMap);
422 | pluginModule = defined[prefix];
423 | }
424 |
425 | //Account for relative paths if there is a base name.
426 | if (name) {
427 | if (prefix) {
428 | if (pluginModule && pluginModule.normalize) {
429 | //Plugin is loaded, use its normalize method.
430 | normalizedName = pluginModule.normalize(name, function (name) {
431 | return normalize(name, parentName, applyMap);
432 | });
433 | } else {
434 | normalizedName = normalize(name, parentName, applyMap);
435 | }
436 | } else {
437 | //A regular module.
438 | normalizedName = normalize(name, parentName, applyMap);
439 |
440 | //Normalized name may be a plugin ID due to map config
441 | //application in normalize. The map config values must
442 | //already be normalized, so do not need to redo that part.
443 | nameParts = splitPrefix(normalizedName);
444 | prefix = nameParts[0];
445 | normalizedName = nameParts[1];
446 | isNormalized = true;
447 |
448 | url = context.nameToUrl(normalizedName);
449 | }
450 | }
451 |
452 | //If the id is a plugin id that cannot be determined if it needs
453 | //normalization, stamp it with a unique ID so two matching relative
454 | //ids that may conflict can be separate.
455 | suffix = prefix && !pluginModule && !isNormalized ?
456 | '_unnormalized' + (unnormalizedCounter += 1) :
457 | '';
458 |
459 | return {
460 | prefix: prefix,
461 | name: normalizedName,
462 | parentMap: parentModuleMap,
463 | unnormalized: !!suffix,
464 | url: url,
465 | originalName: originalName,
466 | isDefine: isDefine,
467 | id: (prefix ?
468 | prefix + '!' + normalizedName :
469 | normalizedName) + suffix
470 | };
471 | }
472 |
473 | function getModule(depMap) {
474 | var id = depMap.id,
475 | mod = registry[id];
476 |
477 | if (!mod) {
478 | mod = registry[id] = new context.Module(depMap);
479 | }
480 |
481 | return mod;
482 | }
483 |
484 | function on(depMap, name, fn) {
485 | var id = depMap.id,
486 | mod = registry[id];
487 |
488 | if (hasProp(defined, id) &&
489 | (!mod || mod.defineEmitComplete)) {
490 | if (name === 'defined') {
491 | fn(defined[id]);
492 | }
493 | } else {
494 | getModule(depMap).on(name, fn);
495 | }
496 | }
497 |
498 | function onError(err, errback) {
499 | var ids = err.requireModules,
500 | notified = false;
501 |
502 | if (errback) {
503 | errback(err);
504 | } else {
505 | each(ids, function (id) {
506 | var mod = registry[id];
507 | if (mod) {
508 | //Set error on module, so it skips timeout checks.
509 | mod.error = err;
510 | if (mod.events.error) {
511 | notified = true;
512 | mod.emit('error', err);
513 | }
514 | }
515 | });
516 |
517 | if (!notified) {
518 | req.onError(err);
519 | }
520 | }
521 | }
522 |
523 | /**
524 | * Internal method to transfer globalQueue items to this context's
525 | * defQueue.
526 | */
527 | function takeGlobalQueue() {
528 | //Push all the globalDefQueue items into the context's defQueue
529 | if (globalDefQueue.length) {
530 | //Array splice in the values since the context code has a
531 | //local var ref to defQueue, so cannot just reassign the one
532 | //on context.
533 | apsp.apply(defQueue,
534 | [defQueue.length - 1, 0].concat(globalDefQueue));
535 | globalDefQueue = [];
536 | }
537 | }
538 |
539 | handlers = {
540 | 'require': function (mod) {
541 | if (mod.require) {
542 | return mod.require;
543 | } else {
544 | return (mod.require = context.makeRequire(mod.map));
545 | }
546 | },
547 | 'exports': function (mod) {
548 | mod.usingExports = true;
549 | if (mod.map.isDefine) {
550 | if (mod.exports) {
551 | return mod.exports;
552 | } else {
553 | return (mod.exports = defined[mod.map.id] = {});
554 | }
555 | }
556 | },
557 | 'module': function (mod) {
558 | if (mod.module) {
559 | return mod.module;
560 | } else {
561 | return (mod.module = {
562 | id: mod.map.id,
563 | uri: mod.map.url,
564 | config: function () {
565 | return (config.config && config.config[mod.map.id]) || {};
566 | },
567 | exports: defined[mod.map.id]
568 | });
569 | }
570 | }
571 | };
572 |
573 | function cleanRegistry(id) {
574 | //Clean up machinery used for waiting modules.
575 | delete registry[id];
576 | }
577 |
578 | function breakCycle(mod, traced, processed) {
579 | var id = mod.map.id;
580 |
581 | if (mod.error) {
582 | mod.emit('error', mod.error);
583 | } else {
584 | traced[id] = true;
585 | each(mod.depMaps, function (depMap, i) {
586 | var depId = depMap.id,
587 | dep = registry[depId];
588 |
589 | //Only force things that have not completed
590 | //being defined, so still in the registry,
591 | //and only if it has not been matched up
592 | //in the module already.
593 | if (dep && !mod.depMatched[i] && !processed[depId]) {
594 | if (traced[depId]) {
595 | mod.defineDep(i, defined[depId]);
596 | mod.check(); //pass false?
597 | } else {
598 | breakCycle(dep, traced, processed);
599 | }
600 | }
601 | });
602 | processed[id] = true;
603 | }
604 | }
605 |
606 | function checkLoaded() {
607 | var map, modId, err, usingPathFallback,
608 | waitInterval = config.waitSeconds * 1000,
609 | //It is possible to disable the wait interval by using waitSeconds of 0.
610 | expired = waitInterval && (context.startTime + waitInterval) < new Date().getTime(),
611 | noLoads = [],
612 | reqCalls = [],
613 | stillLoading = false,
614 | needCycleCheck = true;
615 |
616 | //Do not bother if this call was a result of a cycle break.
617 | if (inCheckLoaded) {
618 | return;
619 | }
620 |
621 | inCheckLoaded = true;
622 |
623 | //Figure out the state of all the modules.
624 | eachProp(registry, function (mod) {
625 | map = mod.map;
626 | modId = map.id;
627 |
628 | //Skip things that are not enabled or in error state.
629 | if (!mod.enabled) {
630 | return;
631 | }
632 |
633 | if (!map.isDefine) {
634 | reqCalls.push(mod);
635 | }
636 |
637 | if (!mod.error) {
638 | //If the module should be executed, and it has not
639 | //been inited and time is up, remember it.
640 | if (!mod.inited && expired) {
641 | if (hasPathFallback(modId)) {
642 | usingPathFallback = true;
643 | stillLoading = true;
644 | } else {
645 | noLoads.push(modId);
646 | removeScript(modId);
647 | }
648 | } else if (!mod.inited && mod.fetched && map.isDefine) {
649 | stillLoading = true;
650 | if (!map.prefix) {
651 | //No reason to keep looking for unfinished
652 | //loading. If the only stillLoading is a
653 | //plugin resource though, keep going,
654 | //because it may be that a plugin resource
655 | //is waiting on a non-plugin cycle.
656 | return (needCycleCheck = false);
657 | }
658 | }
659 | }
660 | });
661 |
662 | if (expired && noLoads.length) {
663 | //If wait time expired, throw error of unloaded modules.
664 | err = makeError('timeout', 'Load timeout for modules: ' + noLoads, null, noLoads);
665 | err.contextName = context.contextName;
666 | return onError(err);
667 | }
668 |
669 | //Not expired, check for a cycle.
670 | if (needCycleCheck) {
671 | each(reqCalls, function (mod) {
672 | breakCycle(mod, {}, {});
673 | });
674 | }
675 |
676 | //If still waiting on loads, and the waiting load is something
677 | //other than a plugin resource, or there are still outstanding
678 | //scripts, then just try back later.
679 | if ((!expired || usingPathFallback) && stillLoading) {
680 | //Something is still waiting to load. Wait for it, but only
681 | //if a timeout is not already in effect.
682 | if ((isBrowser || isWebWorker) && !checkLoadedTimeoutId) {
683 | checkLoadedTimeoutId = setTimeout(function () {
684 | checkLoadedTimeoutId = 0;
685 | checkLoaded();
686 | }, 50);
687 | }
688 | }
689 |
690 | inCheckLoaded = false;
691 | }
692 |
693 | Module = function (map) {
694 | this.events = undefEvents[map.id] || {};
695 | this.map = map;
696 | this.shim = config.shim[map.id];
697 | this.depExports = [];
698 | this.depMaps = [];
699 | this.depMatched = [];
700 | this.pluginMaps = {};
701 | this.depCount = 0;
702 |
703 | /* this.exports this.factory
704 | this.depMaps = [],
705 | this.enabled, this.fetched
706 | */
707 | };
708 |
709 | Module.prototype = {
710 | init: function (depMaps, factory, errback, options) {
711 | options = options || {};
712 |
713 | //Do not do more inits if already done. Can happen if there
714 | //are multiple define calls for the same module. That is not
715 | //a normal, common case, but it is also not unexpected.
716 | if (this.inited) {
717 | return;
718 | }
719 |
720 | this.factory = factory;
721 |
722 | if (errback) {
723 | //Register for errors on this module.
724 | this.on('error', errback);
725 | } else if (this.events.error) {
726 | //If no errback already, but there are error listeners
727 | //on this module, set up an errback to pass to the deps.
728 | errback = bind(this, function (err) {
729 | this.emit('error', err);
730 | });
731 | }
732 |
733 | //Do a copy of the dependency array, so that
734 | //source inputs are not modified. For example
735 | //"shim" deps are passed in here directly, and
736 | //doing a direct modification of the depMaps array
737 | //would affect that config.
738 | this.depMaps = depMaps && depMaps.slice(0);
739 |
740 | this.errback = errback;
741 |
742 | //Indicate this module has be initialized
743 | this.inited = true;
744 |
745 | this.ignore = options.ignore;
746 |
747 | //Could have option to init this module in enabled mode,
748 | //or could have been previously marked as enabled. However,
749 | //the dependencies are not known until init is called. So
750 | //if enabled previously, now trigger dependencies as enabled.
751 | if (options.enabled || this.enabled) {
752 | //Enable this module and dependencies.
753 | //Will call this.check()
754 | this.enable();
755 | } else {
756 | this.check();
757 | }
758 | },
759 |
760 | defineDep: function (i, depExports) {
761 | //Because of cycles, defined callback for a given
762 | //export can be called more than once.
763 | if (!this.depMatched[i]) {
764 | this.depMatched[i] = true;
765 | this.depCount -= 1;
766 | this.depExports[i] = depExports;
767 | }
768 | },
769 |
770 | fetch: function () {
771 | if (this.fetched) {
772 | return;
773 | }
774 | this.fetched = true;
775 |
776 | context.startTime = (new Date()).getTime();
777 |
778 | var map = this.map;
779 |
780 | //If the manager is for a plugin managed resource,
781 | //ask the plugin to load it now.
782 | if (this.shim) {
783 | context.makeRequire(this.map, {
784 | enableBuildCallback: true
785 | })(this.shim.deps || [], bind(this, function () {
786 | return map.prefix ? this.callPlugin() : this.load();
787 | }));
788 | } else {
789 | //Regular dependency.
790 | return map.prefix ? this.callPlugin() : this.load();
791 | }
792 | },
793 |
794 | load: function () {
795 | var url = this.map.url;
796 |
797 | //Regular dependency.
798 | if (!urlFetched[url]) {
799 | urlFetched[url] = true;
800 | context.load(this.map.id, url);
801 | }
802 | },
803 |
804 | /**
805 | * Checks is the module is ready to define itself, and if so,
806 | * define it.
807 | */
808 | check: function () {
809 | if (!this.enabled || this.enabling) {
810 | return;
811 | }
812 |
813 | var err, cjsModule,
814 | id = this.map.id,
815 | depExports = this.depExports,
816 | exports = this.exports,
817 | factory = this.factory;
818 |
819 | if (!this.inited) {
820 | this.fetch();
821 | } else if (this.error) {
822 | this.emit('error', this.error);
823 | } else if (!this.defining) {
824 | //The factory could trigger another require call
825 | //that would result in checking this module to
826 | //define itself again. If already in the process
827 | //of doing that, skip this work.
828 | this.defining = true;
829 |
830 | if (this.depCount < 1 && !this.defined) {
831 | if (isFunction(factory)) {
832 | //If there is an error listener, favor passing
833 | //to that instead of throwing an error.
834 | if (this.events.error) {
835 | try {
836 | exports = context.execCb(id, factory, depExports, exports);
837 | } catch (e) {
838 | err = e;
839 | }
840 | } else {
841 | exports = context.execCb(id, factory, depExports, exports);
842 | }
843 |
844 | if (this.map.isDefine) {
845 | //If setting exports via 'module' is in play,
846 | //favor that over return value and exports. After that,
847 | //favor a non-undefined return value over exports use.
848 | cjsModule = this.module;
849 | if (cjsModule &&
850 | cjsModule.exports !== undefined &&
851 | //Make sure it is not already the exports value
852 | cjsModule.exports !== this.exports) {
853 | exports = cjsModule.exports;
854 | } else if (exports === undefined && this.usingExports) {
855 | //exports already set the defined value.
856 | exports = this.exports;
857 | }
858 | }
859 |
860 | if (err) {
861 | err.requireMap = this.map;
862 | err.requireModules = [this.map.id];
863 | err.requireType = 'define';
864 | return onError((this.error = err));
865 | }
866 |
867 | } else {
868 | //Just a literal value
869 | exports = factory;
870 | }
871 |
872 | this.exports = exports;
873 |
874 | if (this.map.isDefine && !this.ignore) {
875 | defined[id] = exports;
876 |
877 | if (req.onResourceLoad) {
878 | req.onResourceLoad(context, this.map, this.depMaps);
879 | }
880 | }
881 |
882 | //Clean up
883 | delete registry[id];
884 |
885 | this.defined = true;
886 | }
887 |
888 | //Finished the define stage. Allow calling check again
889 | //to allow define notifications below in the case of a
890 | //cycle.
891 | this.defining = false;
892 |
893 | if (this.defined && !this.defineEmitted) {
894 | this.defineEmitted = true;
895 | this.emit('defined', this.exports);
896 | this.defineEmitComplete = true;
897 | }
898 |
899 | }
900 | },
901 |
902 | callPlugin: function () {
903 | var map = this.map,
904 | id = map.id,
905 | //Map already normalized the prefix.
906 | pluginMap = makeModuleMap(map.prefix);
907 |
908 | //Mark this as a dependency for this plugin, so it
909 | //can be traced for cycles.
910 | this.depMaps.push(pluginMap);
911 |
912 | on(pluginMap, 'defined', bind(this, function (plugin) {
913 | var load, normalizedMap, normalizedMod,
914 | name = this.map.name,
915 | parentName = this.map.parentMap ? this.map.parentMap.name : null,
916 | localRequire = context.makeRequire(map.parentMap, {
917 | enableBuildCallback: true,
918 | skipMap: true
919 | });
920 |
921 | //If current map is not normalized, wait for that
922 | //normalized name to load instead of continuing.
923 | if (this.map.unnormalized) {
924 | //Normalize the ID if the plugin allows it.
925 | if (plugin.normalize) {
926 | name = plugin.normalize(name, function (name) {
927 | return normalize(name, parentName, true);
928 | }) || '';
929 | }
930 |
931 | //prefix and name should already be normalized, no need
932 | //for applying map config again either.
933 | normalizedMap = makeModuleMap(map.prefix + '!' + name,
934 | this.map.parentMap);
935 | on(normalizedMap,
936 | 'defined', bind(this, function (value) {
937 | this.init([], function () { return value; }, null, {
938 | enabled: true,
939 | ignore: true
940 | });
941 | }));
942 |
943 | normalizedMod = registry[normalizedMap.id];
944 | if (normalizedMod) {
945 | //Mark this as a dependency for this plugin, so it
946 | //can be traced for cycles.
947 | this.depMaps.push(normalizedMap);
948 |
949 | if (this.events.error) {
950 | normalizedMod.on('error', bind(this, function (err) {
951 | this.emit('error', err);
952 | }));
953 | }
954 | normalizedMod.enable();
955 | }
956 |
957 | return;
958 | }
959 |
960 | load = bind(this, function (value) {
961 | this.init([], function () { return value; }, null, {
962 | enabled: true
963 | });
964 | });
965 |
966 | load.error = bind(this, function (err) {
967 | this.inited = true;
968 | this.error = err;
969 | err.requireModules = [id];
970 |
971 | //Remove temp unnormalized modules for this module,
972 | //since they will never be resolved otherwise now.
973 | eachProp(registry, function (mod) {
974 | if (mod.map.id.indexOf(id + '_unnormalized') === 0) {
975 | cleanRegistry(mod.map.id);
976 | }
977 | });
978 |
979 | onError(err);
980 | });
981 |
982 | //Allow plugins to load other code without having to know the
983 | //context or how to 'complete' the load.
984 | load.fromText = bind(this, function (text, textAlt) {
985 | /*jslint evil: true */
986 | var moduleName = map.name,
987 | moduleMap = makeModuleMap(moduleName),
988 | hasInteractive = useInteractive;
989 |
990 | //As of 2.1.0, support just passing the text, to reinforce
991 | //fromText only being called once per resource. Still
992 | //support old style of passing moduleName but discard
993 | //that moduleName in favor of the internal ref.
994 | if (textAlt) {
995 | text = textAlt;
996 | }
997 |
998 | //Turn off interactive script matching for IE for any define
999 | //calls in the text, then turn it back on at the end.
1000 | if (hasInteractive) {
1001 | useInteractive = false;
1002 | }
1003 |
1004 | //Prime the system by creating a module instance for
1005 | //it.
1006 | getModule(moduleMap);
1007 |
1008 | try {
1009 | req.exec(text);
1010 | } catch (e) {
1011 | throw new Error('fromText eval for ' + moduleName +
1012 | ' failed: ' + e);
1013 | }
1014 |
1015 | if (hasInteractive) {
1016 | useInteractive = true;
1017 | }
1018 |
1019 | //Mark this as a dependency for the plugin
1020 | //resource
1021 | this.depMaps.push(moduleMap);
1022 |
1023 | //Support anonymous modules.
1024 | context.completeLoad(moduleName);
1025 |
1026 | //Bind the value of that module to the value for this
1027 | //resource ID.
1028 | localRequire([moduleName], load);
1029 | });
1030 |
1031 | //Use parentName here since the plugin's name is not reliable,
1032 | //could be some weird string with no path that actually wants to
1033 | //reference the parentName's path.
1034 | plugin.load(map.name, localRequire, load, config);
1035 | }));
1036 |
1037 | context.enable(pluginMap, this);
1038 | this.pluginMaps[pluginMap.id] = pluginMap;
1039 | },
1040 |
1041 | enable: function () {
1042 | this.enabled = true;
1043 |
1044 | //Set flag mentioning that the module is enabling,
1045 | //so that immediate calls to the defined callbacks
1046 | //for dependencies do not trigger inadvertent load
1047 | //with the depCount still being zero.
1048 | this.enabling = true;
1049 |
1050 | //Enable each dependency
1051 | each(this.depMaps, bind(this, function (depMap, i) {
1052 | var id, mod, handler;
1053 |
1054 | if (typeof depMap === 'string') {
1055 | //Dependency needs to be converted to a depMap
1056 | //and wired up to this module.
1057 | depMap = makeModuleMap(depMap,
1058 | (this.map.isDefine ? this.map : this.map.parentMap),
1059 | false,
1060 | !this.skipMap);
1061 | this.depMaps[i] = depMap;
1062 |
1063 | handler = handlers[depMap.id];
1064 |
1065 | if (handler) {
1066 | this.depExports[i] = handler(this);
1067 | return;
1068 | }
1069 |
1070 | this.depCount += 1;
1071 |
1072 | on(depMap, 'defined', bind(this, function (depExports) {
1073 | this.defineDep(i, depExports);
1074 | this.check();
1075 | }));
1076 |
1077 | if (this.errback) {
1078 | on(depMap, 'error', this.errback);
1079 | }
1080 | }
1081 |
1082 | id = depMap.id;
1083 | mod = registry[id];
1084 |
1085 | //Skip special modules like 'require', 'exports', 'module'
1086 | //Also, don't call enable if it is already enabled,
1087 | //important in circular dependency cases.
1088 | if (!handlers[id] && mod && !mod.enabled) {
1089 | context.enable(depMap, this);
1090 | }
1091 | }));
1092 |
1093 | //Enable each plugin that is used in
1094 | //a dependency
1095 | eachProp(this.pluginMaps, bind(this, function (pluginMap) {
1096 | var mod = registry[pluginMap.id];
1097 | if (mod && !mod.enabled) {
1098 | context.enable(pluginMap, this);
1099 | }
1100 | }));
1101 |
1102 | this.enabling = false;
1103 |
1104 | this.check();
1105 | },
1106 |
1107 | on: function (name, cb) {
1108 | var cbs = this.events[name];
1109 | if (!cbs) {
1110 | cbs = this.events[name] = [];
1111 | }
1112 | cbs.push(cb);
1113 | },
1114 |
1115 | emit: function (name, evt) {
1116 | each(this.events[name], function (cb) {
1117 | cb(evt);
1118 | });
1119 | if (name === 'error') {
1120 | //Now that the error handler was triggered, remove
1121 | //the listeners, since this broken Module instance
1122 | //can stay around for a while in the registry.
1123 | delete this.events[name];
1124 | }
1125 | }
1126 | };
1127 |
1128 | function callGetModule(args) {
1129 | getModule(makeModuleMap(args[0], null, true)).init(args[1], args[2]);
1130 | }
1131 |
1132 | function removeListener(node, func, name, ieName) {
1133 | //Favor detachEvent because of IE9
1134 | //issue, see attachEvent/addEventListener comment elsewhere
1135 | //in this file.
1136 | if (node.detachEvent && !isOpera) {
1137 | //Probably IE. If not it will throw an error, which will be
1138 | //useful to know.
1139 | if (ieName) {
1140 | node.detachEvent(ieName, func);
1141 | }
1142 | } else {
1143 | node.removeEventListener(name, func, false);
1144 | }
1145 | }
1146 |
1147 | /**
1148 | * Given an event from a script node, get the requirejs info from it,
1149 | * and then removes the event listeners on the node.
1150 | * @param {Event} evt
1151 | * @returns {Object}
1152 | */
1153 | function getScriptData(evt) {
1154 | //Using currentTarget instead of target for Firefox 2.0's sake. Not
1155 | //all old browsers will be supported, but this one was easy enough
1156 | //to support and still makes sense.
1157 | var node = evt.currentTarget || evt.srcElement;
1158 |
1159 | //Remove the listeners once here.
1160 | removeListener(node, context.onScriptLoad, 'load', 'onreadystatechange');
1161 | removeListener(node, context.onScriptError, 'error');
1162 |
1163 | return {
1164 | node: node,
1165 | id: node && node.getAttribute('data-requiremodule')
1166 | };
1167 | }
1168 |
1169 | function intakeDefines() {
1170 | var args;
1171 |
1172 | //Any defined modules in the global queue, intake them now.
1173 | takeGlobalQueue();
1174 |
1175 | //Make sure any remaining defQueue items get properly processed.
1176 | while (defQueue.length) {
1177 | args = defQueue.shift();
1178 | if (args[0] === null) {
1179 | return onError(makeError('mismatch', 'Mismatched anonymous define() module: ' + args[args.length - 1]));
1180 | } else {
1181 | //args are id, deps, factory. Should be normalized by the
1182 | //define() function.
1183 | callGetModule(args);
1184 | }
1185 | }
1186 | }
1187 |
1188 | context = {
1189 | config: config,
1190 | contextName: contextName,
1191 | registry: registry,
1192 | defined: defined,
1193 | urlFetched: urlFetched,
1194 | defQueue: defQueue,
1195 | Module: Module,
1196 | makeModuleMap: makeModuleMap,
1197 | nextTick: req.nextTick,
1198 |
1199 | /**
1200 | * Set a configuration for the context.
1201 | * @param {Object} cfg config object to integrate.
1202 | */
1203 | configure: function (cfg) {
1204 | //Make sure the baseUrl ends in a slash.
1205 | if (cfg.baseUrl) {
1206 | if (cfg.baseUrl.charAt(cfg.baseUrl.length - 1) !== '/') {
1207 | cfg.baseUrl += '/';
1208 | }
1209 | }
1210 |
1211 | //Save off the paths and packages since they require special processing,
1212 | //they are additive.
1213 | var pkgs = config.pkgs,
1214 | shim = config.shim,
1215 | objs = {
1216 | paths: true,
1217 | config: true,
1218 | map: true
1219 | };
1220 |
1221 | eachProp(cfg, function (value, prop) {
1222 | if (objs[prop]) {
1223 | if (prop === 'map') {
1224 | mixin(config[prop], value, true, true);
1225 | } else {
1226 | mixin(config[prop], value, true);
1227 | }
1228 | } else {
1229 | config[prop] = value;
1230 | }
1231 | });
1232 |
1233 | //Merge shim
1234 | if (cfg.shim) {
1235 | eachProp(cfg.shim, function (value, id) {
1236 | //Normalize the structure
1237 | if (isArray(value)) {
1238 | value = {
1239 | deps: value
1240 | };
1241 | }
1242 | if (value.exports && !value.exportsFn) {
1243 | value.exportsFn = context.makeShimExports(value);
1244 | }
1245 | shim[id] = value;
1246 | });
1247 | config.shim = shim;
1248 | }
1249 |
1250 | //Adjust packages if necessary.
1251 | if (cfg.packages) {
1252 | each(cfg.packages, function (pkgObj) {
1253 | var location;
1254 |
1255 | pkgObj = typeof pkgObj === 'string' ? { name: pkgObj } : pkgObj;
1256 | location = pkgObj.location;
1257 |
1258 | //Create a brand new object on pkgs, since currentPackages can
1259 | //be passed in again, and config.pkgs is the internal transformed
1260 | //state for all package configs.
1261 | pkgs[pkgObj.name] = {
1262 | name: pkgObj.name,
1263 | location: location || pkgObj.name,
1264 | //Remove leading dot in main, so main paths are normalized,
1265 | //and remove any trailing .js, since different package
1266 | //envs have different conventions: some use a module name,
1267 | //some use a file name.
1268 | main: (pkgObj.main || 'main')
1269 | .replace(currDirRegExp, '')
1270 | .replace(jsSuffixRegExp, '')
1271 | };
1272 | });
1273 |
1274 | //Done with modifications, assing packages back to context config
1275 | config.pkgs = pkgs;
1276 | }
1277 |
1278 | //If there are any "waiting to execute" modules in the registry,
1279 | //update the maps for them, since their info, like URLs to load,
1280 | //may have changed.
1281 | eachProp(registry, function (mod, id) {
1282 | //If module already has init called, since it is too
1283 | //late to modify them, and ignore unnormalized ones
1284 | //since they are transient.
1285 | if (!mod.inited && !mod.map.unnormalized) {
1286 | mod.map = makeModuleMap(id);
1287 | }
1288 | });
1289 |
1290 | //If a deps array or a config callback is specified, then call
1291 | //require with those args. This is useful when require is defined as a
1292 | //config object before require.js is loaded.
1293 | if (cfg.deps || cfg.callback) {
1294 | context.require(cfg.deps || [], cfg.callback);
1295 | }
1296 | },
1297 |
1298 | makeShimExports: function (value) {
1299 | function fn() {
1300 | var ret;
1301 | if (value.init) {
1302 | ret = value.init.apply(global, arguments);
1303 | }
1304 | return ret || getGlobal(value.exports);
1305 | }
1306 | return fn;
1307 | },
1308 |
1309 | makeRequire: function (relMap, options) {
1310 | options = options || {};
1311 |
1312 | function localRequire(deps, callback, errback) {
1313 | var id, map, requireMod;
1314 |
1315 | if (options.enableBuildCallback && callback && isFunction(callback)) {
1316 | callback.__requireJsBuild = true;
1317 | }
1318 |
1319 | if (typeof deps === 'string') {
1320 | if (isFunction(callback)) {
1321 | //Invalid call
1322 | return onError(makeError('requireargs', 'Invalid require call'), errback);
1323 | }
1324 |
1325 | //If require|exports|module are requested, get the
1326 | //value for them from the special handlers. Caveat:
1327 | //this only works while module is being defined.
1328 | if (relMap && handlers[deps]) {
1329 | return handlers[deps](registry[relMap.id]);
1330 | }
1331 |
1332 | //Synchronous access to one module. If require.get is
1333 | //available (as in the TrieNode adapter), prefer that.
1334 | if (req.get) {
1335 | return req.get(context, deps, relMap);
1336 | }
1337 |
1338 | //Normalize module name, if it contains . or ..
1339 | map = makeModuleMap(deps, relMap, false, true);
1340 | id = map.id;
1341 |
1342 | if (!hasProp(defined, id)) {
1343 | return onError(makeError('notloaded', 'Module name "' +
1344 | id +
1345 | '" has not been loaded yet for context: ' +
1346 | contextName +
1347 | (relMap ? '' : '. Use require([])')));
1348 | }
1349 | return defined[id];
1350 | }
1351 |
1352 | //Grab defines waiting in the global queue.
1353 | intakeDefines();
1354 |
1355 | //Mark all the dependencies as needing to be loaded.
1356 | context.nextTick(function () {
1357 | //Some defines could have been added since the
1358 | //require call, collect them.
1359 | intakeDefines();
1360 |
1361 | requireMod = getModule(makeModuleMap(null, relMap));
1362 |
1363 | //Store if map config should be applied to this require
1364 | //call for dependencies.
1365 | requireMod.skipMap = options.skipMap;
1366 |
1367 | requireMod.init(deps, callback, errback, {
1368 | enabled: true
1369 | });
1370 |
1371 | checkLoaded();
1372 | });
1373 |
1374 | return localRequire;
1375 | }
1376 |
1377 | mixin(localRequire, {
1378 | isBrowser: isBrowser,
1379 |
1380 | /**
1381 | * Converts a module name + .extension into an URL path.
1382 | * *Requires* the use of a module name. It does not support using
1383 | * plain URLs like nameToUrl.
1384 | */
1385 | toUrl: function (moduleNamePlusExt) {
1386 | var index = moduleNamePlusExt.lastIndexOf('.'),
1387 | ext = null;
1388 |
1389 | if (index !== -1) {
1390 | ext = moduleNamePlusExt.substring(index, moduleNamePlusExt.length);
1391 | moduleNamePlusExt = moduleNamePlusExt.substring(0, index);
1392 | }
1393 |
1394 | return context.nameToUrl(normalize(moduleNamePlusExt,
1395 | relMap && relMap.id, true), ext);
1396 | },
1397 |
1398 | defined: function (id) {
1399 | return hasProp(defined, makeModuleMap(id, relMap, false, true).id);
1400 | },
1401 |
1402 | specified: function (id) {
1403 | id = makeModuleMap(id, relMap, false, true).id;
1404 | return hasProp(defined, id) || hasProp(registry, id);
1405 | }
1406 | });
1407 |
1408 | //Only allow undef on top level require calls
1409 | if (!relMap) {
1410 | localRequire.undef = function (id) {
1411 | //Bind any waiting define() calls to this context,
1412 | //fix for #408
1413 | takeGlobalQueue();
1414 |
1415 | var map = makeModuleMap(id, relMap, true),
1416 | mod = registry[id];
1417 |
1418 | delete defined[id];
1419 | delete urlFetched[map.url];
1420 | delete undefEvents[id];
1421 |
1422 | if (mod) {
1423 | //Hold on to listeners in case the
1424 | //module will be attempted to be reloaded
1425 | //using a different config.
1426 | if (mod.events.defined) {
1427 | undefEvents[id] = mod.events;
1428 | }
1429 |
1430 | cleanRegistry(id);
1431 | }
1432 | };
1433 | }
1434 |
1435 | return localRequire;
1436 | },
1437 |
1438 | /**
1439 | * Called to enable a module if it is still in the registry
1440 | * awaiting enablement. parent module is passed in for context,
1441 | * used by the optimizer.
1442 | */
1443 | enable: function (depMap, parent) {
1444 | var mod = registry[depMap.id];
1445 | if (mod) {
1446 | getModule(depMap).enable();
1447 | }
1448 | },
1449 |
1450 | /**
1451 | * Internal method used by environment adapters to complete a load event.
1452 | * A load event could be a script load or just a load pass from a synchronous
1453 | * load call.
1454 | * @param {String} moduleName the name of the module to potentially complete.
1455 | */
1456 | completeLoad: function (moduleName) {
1457 | var found, args, mod,
1458 | shim = config.shim[moduleName] || {},
1459 | shExports = shim.exports;
1460 |
1461 | takeGlobalQueue();
1462 |
1463 | while (defQueue.length) {
1464 | args = defQueue.shift();
1465 | if (args[0] === null) {
1466 | args[0] = moduleName;
1467 | //If already found an anonymous module and bound it
1468 | //to this name, then this is some other anon module
1469 | //waiting for its completeLoad to fire.
1470 | if (found) {
1471 | break;
1472 | }
1473 | found = true;
1474 | } else if (args[0] === moduleName) {
1475 | //Found matching define call for this script!
1476 | found = true;
1477 | }
1478 |
1479 | callGetModule(args);
1480 | }
1481 |
1482 | //Do this after the cycle of callGetModule in case the result
1483 | //of those calls/init calls changes the registry.
1484 | mod = registry[moduleName];
1485 |
1486 | if (!found && !defined[moduleName] && mod && !mod.inited) {
1487 | if (config.enforceDefine && (!shExports || !getGlobal(shExports))) {
1488 | if (hasPathFallback(moduleName)) {
1489 | return;
1490 | } else {
1491 | return onError(makeError('nodefine',
1492 | 'No define call for ' + moduleName,
1493 | null,
1494 | [moduleName]));
1495 | }
1496 | } else {
1497 | //A script that does not call define(), so just simulate
1498 | //the call for it.
1499 | callGetModule([moduleName, (shim.deps || []), shim.exportsFn]);
1500 | }
1501 | }
1502 |
1503 | checkLoaded();
1504 | },
1505 |
1506 | /**
1507 | * Converts a module name to a file path. Supports cases where
1508 | * moduleName may actually be just an URL.
1509 | * Note that it **does not** call normalize on the moduleName,
1510 | * it is assumed to have already been normalized. This is an
1511 | * internal API, not a public one. Use toUrl for the public API.
1512 | */
1513 | nameToUrl: function (moduleName, ext) {
1514 | var paths, pkgs, pkg, pkgPath, syms, i, parentModule, url,
1515 | parentPath;
1516 |
1517 | //If a colon is in the URL, it indicates a protocol is used and it is just
1518 | //an URL to a file, or if it starts with a slash, contains a query arg (i.e. ?)
1519 | //or ends with .js, then assume the user meant to use an url and not a module id.
1520 | //The slash is important for protocol-less URLs as well as full paths.
1521 | if (req.jsExtRegExp.test(moduleName)) {
1522 | //Just a plain path, not module name lookup, so just return it.
1523 | //Add extension if it is included. This is a bit wonky, only non-.js things pass
1524 | //an extension, this method probably needs to be reworked.
1525 | url = moduleName + (ext || '');
1526 | } else {
1527 | //A module that needs to be converted to a path.
1528 | paths = config.paths;
1529 | pkgs = config.pkgs;
1530 |
1531 | syms = moduleName.split('/');
1532 | //For each module name segment, see if there is a path
1533 | //registered for it. Start with most specific name
1534 | //and work up from it.
1535 | for (i = syms.length; i > 0; i -= 1) {
1536 | parentModule = syms.slice(0, i).join('/');
1537 | pkg = pkgs[parentModule];
1538 | parentPath = paths[parentModule];
1539 | if (parentPath) {
1540 | //If an array, it means there are a few choices,
1541 | //Choose the one that is desired
1542 | if (isArray(parentPath)) {
1543 | parentPath = parentPath[0];
1544 | }
1545 | syms.splice(0, i, parentPath);
1546 | break;
1547 | } else if (pkg) {
1548 | //If module name is just the package name, then looking
1549 | //for the main module.
1550 | if (moduleName === pkg.name) {
1551 | pkgPath = pkg.location + '/' + pkg.main;
1552 | } else {
1553 | pkgPath = pkg.location;
1554 | }
1555 | syms.splice(0, i, pkgPath);
1556 | break;
1557 | }
1558 | }
1559 |
1560 | //Join the path parts together, then figure out if baseUrl is needed.
1561 | url = syms.join('/');
1562 | url += (ext || (/\?/.test(url) ? '' : '.js'));
1563 | url = (url.charAt(0) === '/' || url.match(/^[\w\+\.\-]+:/) ? '' : config.baseUrl) + url;
1564 | }
1565 |
1566 | return config.urlArgs ? url +
1567 | ((url.indexOf('?') === -1 ? '?' : '&') +
1568 | config.urlArgs) : url;
1569 | },
1570 |
1571 | //Delegates to req.load. Broken out as a separate function to
1572 | //allow overriding in the optimizer.
1573 | load: function (id, url) {
1574 | req.load(context, id, url);
1575 | },
1576 |
1577 | /**
1578 | * Executes a module callack function. Broken out as a separate function
1579 | * solely to allow the build system to sequence the files in the built
1580 | * layer in the right sequence.
1581 | *
1582 | * @private
1583 | */
1584 | execCb: function (name, callback, args, exports) {
1585 | return callback.apply(exports, args);
1586 | },
1587 |
1588 | /**
1589 | * callback for script loads, used to check status of loading.
1590 | *
1591 | * @param {Event} evt the event from the browser for the script
1592 | * that was loaded.
1593 | */
1594 | onScriptLoad: function (evt) {
1595 | //Using currentTarget instead of target for Firefox 2.0's sake. Not
1596 | //all old browsers will be supported, but this one was easy enough
1597 | //to support and still makes sense.
1598 | if (evt.type === 'load' ||
1599 | (readyRegExp.test((evt.currentTarget || evt.srcElement).readyState))) {
1600 | //Reset interactive script so a script node is not held onto for
1601 | //to long.
1602 | interactiveScript = null;
1603 |
1604 | //Pull out the name of the module and the context.
1605 | var data = getScriptData(evt);
1606 | context.completeLoad(data.id);
1607 | }
1608 | },
1609 |
1610 | /**
1611 | * Callback for script errors.
1612 | */
1613 | onScriptError: function (evt) {
1614 | var data = getScriptData(evt);
1615 | if (!hasPathFallback(data.id)) {
1616 | return onError(makeError('scripterror', 'Script error', evt, [data.id]));
1617 | }
1618 | }
1619 | };
1620 |
1621 | context.require = context.makeRequire();
1622 | return context;
1623 | }
1624 |
1625 | /**
1626 | * Main entry point.
1627 | *
1628 | * If the only argument to require is a string, then the module that
1629 | * is represented by that string is fetched for the appropriate context.
1630 | *
1631 | * If the first argument is an array, then it will be treated as an array
1632 | * of dependency string names to fetch. An optional function callback can
1633 | * be specified to execute when all of those dependencies are available.
1634 | *
1635 | * Make a local req variable to help Caja compliance (it assumes things
1636 | * on a require that are not standardized), and to give a short
1637 | * name for minification/local scope use.
1638 | */
1639 | req = requirejs = function (deps, callback, errback, optional) {
1640 |
1641 | //Find the right context, use default
1642 | var context, config,
1643 | contextName = defContextName;
1644 |
1645 | // Determine if have config object in the call.
1646 | if (!isArray(deps) && typeof deps !== 'string') {
1647 | // deps is a config object
1648 | config = deps;
1649 | if (isArray(callback)) {
1650 | // Adjust args if there are dependencies
1651 | deps = callback;
1652 | callback = errback;
1653 | errback = optional;
1654 | } else {
1655 | deps = [];
1656 | }
1657 | }
1658 |
1659 | if (config && config.context) {
1660 | contextName = config.context;
1661 | }
1662 |
1663 | context = contexts[contextName];
1664 | if (!context) {
1665 | context = contexts[contextName] = req.s.newContext(contextName);
1666 | }
1667 |
1668 | if (config) {
1669 | context.configure(config);
1670 | }
1671 |
1672 | return context.require(deps, callback, errback);
1673 | };
1674 |
1675 | /**
1676 | * Support require.config() to make it easier to cooperate with other
1677 | * AMD loaders on globally agreed names.
1678 | */
1679 | req.config = function (config) {
1680 | return req(config);
1681 | };
1682 |
1683 | /**
1684 | * Execute something after the current tick
1685 | * of the event loop. Override for other envs
1686 | * that have a better solution than setTimeout.
1687 | * @param {Function} fn function to execute later.
1688 | */
1689 | req.nextTick = typeof setTimeout !== 'undefined' ? function (fn) {
1690 | setTimeout(fn, 4);
1691 | } : function (fn) { fn(); };
1692 |
1693 | /**
1694 | * Export require as a global, but only if it does not already exist.
1695 | */
1696 | if (!require) {
1697 | require = req;
1698 | }
1699 |
1700 | req.version = version;
1701 |
1702 | //Used to filter out dependencies that are already paths.
1703 | req.jsExtRegExp = /^\/|:|\?|\.js$/;
1704 | req.isBrowser = isBrowser;
1705 | s = req.s = {
1706 | contexts: contexts,
1707 | newContext: newContext
1708 | };
1709 |
1710 | //Create default context.
1711 | req({});
1712 |
1713 | //Exports some context-sensitive methods on global require.
1714 | each([
1715 | 'toUrl',
1716 | 'undef',
1717 | 'defined',
1718 | 'specified'
1719 | ], function (prop) {
1720 | //Reference from contexts instead of early binding to default context,
1721 | //so that during builds, the latest instance of the default context
1722 | //with its config gets used.
1723 | req[prop] = function () {
1724 | var ctx = contexts[defContextName];
1725 | return ctx.require[prop].apply(ctx, arguments);
1726 | };
1727 | });
1728 |
1729 | if (isBrowser) {
1730 | head = s.head = document.getElementsByTagName('head')[0];
1731 | //If BASE tag is in play, using appendChild is a problem for IE6.
1732 | //When that browser dies, this can be removed. Details in this jQuery bug:
1733 | //http://dev.jquery.com/ticket/2709
1734 | baseElement = document.getElementsByTagName('base')[0];
1735 | if (baseElement) {
1736 | head = s.head = baseElement.parentNode;
1737 | }
1738 | }
1739 |
1740 | /**
1741 | * Any errors that require explicitly generates will be passed to this
1742 | * function. Intercept/override it if you want custom error handling.
1743 | * @param {Error} err the error object.
1744 | */
1745 | req.onError = function (err) {
1746 | throw err;
1747 | };
1748 |
1749 | /**
1750 | * Does the request to load a module for the browser case.
1751 | * Make this a separate function to allow other environments
1752 | * to override it.
1753 | *
1754 | * @param {Object} context the require context to find state.
1755 | * @param {String} moduleName the name of the module.
1756 | * @param {Object} url the URL to the module.
1757 | */
1758 | req.load = function (context, moduleName, url) {
1759 | var config = (context && context.config) || {},
1760 | node;
1761 | if (isBrowser) {
1762 | //In the browser so use a script tag
1763 | node = config.xhtml ?
1764 | document.createElementNS('http://www.w3.org/1999/xhtml', 'html:script') :
1765 | document.createElement('script');
1766 | node.type = config.scriptType || 'text/javascript';
1767 | node.charset = 'utf-8';
1768 | node.async = true;
1769 |
1770 | node.setAttribute('data-requirecontext', context.contextName);
1771 | node.setAttribute('data-requiremodule', moduleName);
1772 |
1773 | //Set up load listener. Test attachEvent first because IE9 has
1774 | //a subtle issue in its addEventListener and script onload firings
1775 | //that do not match the behavior of all other browsers with
1776 | //addEventListener support, which fire the onload event for a
1777 | //script right after the script execution. See:
1778 | //https://connect.microsoft.com/IE/feedback/details/648057/script-onload-event-is-not-fired-immediately-after-script-execution
1779 | //UNFORTUNATELY Opera implements attachEvent but does not follow the script
1780 | //script execution mode.
1781 | if (node.attachEvent &&
1782 | //Check if node.attachEvent is artificially added by custom script or
1783 | //natively supported by browser
1784 | //read https://github.com/jrburke/requirejs/issues/187
1785 | //if we can NOT find [native code] then it must NOT natively supported.
1786 | //in IE8, node.attachEvent does not have toString()
1787 | //Note the test for "[native code" with no closing brace, see:
1788 | //https://github.com/jrburke/requirejs/issues/273
1789 | !(node.attachEvent.toString && node.attachEvent.toString().indexOf('[native code') < 0) &&
1790 | !isOpera) {
1791 | //Probably IE. IE (at least 6-8) do not fire
1792 | //script onload right after executing the script, so
1793 | //we cannot tie the anonymous define call to a name.
1794 | //However, IE reports the script as being in 'interactive'
1795 | //readyState at the time of the define call.
1796 | useInteractive = true;
1797 |
1798 | node.attachEvent('onreadystatechange', context.onScriptLoad);
1799 | //It would be great to add an error handler here to catch
1800 | //404s in IE9+. However, onreadystatechange will fire before
1801 | //the error handler, so that does not help. If addEvenListener
1802 | //is used, then IE will fire error before load, but we cannot
1803 | //use that pathway given the connect.microsoft.com issue
1804 | //mentioned above about not doing the 'script execute,
1805 | //then fire the script load event listener before execute
1806 | //next script' that other browsers do.
1807 | //Best hope: IE10 fixes the issues,
1808 | //and then destroys all installs of IE 6-9.
1809 | //node.attachEvent('onerror', context.onScriptError);
1810 | } else {
1811 | node.addEventListener('load', context.onScriptLoad, false);
1812 | node.addEventListener('error', context.onScriptError, false);
1813 | }
1814 | node.src = url;
1815 |
1816 | //For some cache cases in IE 6-8, the script executes before the end
1817 | //of the appendChild execution, so to tie an anonymous define
1818 | //call to the module name (which is stored on the node), hold on
1819 | //to a reference to this node, but clear after the DOM insertion.
1820 | currentlyAddingScript = node;
1821 | if (baseElement) {
1822 | head.insertBefore(node, baseElement);
1823 | } else {
1824 | head.appendChild(node);
1825 | }
1826 | currentlyAddingScript = null;
1827 |
1828 | return node;
1829 | } else if (isWebWorker) {
1830 | //In a web worker, use importScripts. This is not a very
1831 | //efficient use of importScripts, importScripts will block until
1832 | //its script is downloaded and evaluated. However, if web workers
1833 | //are in play, the expectation that a build has been done so that
1834 | //only one script needs to be loaded anyway. This may need to be
1835 | //reevaluated if other use cases become common.
1836 | importScripts(url);
1837 |
1838 | //Account for anonymous modules
1839 | context.completeLoad(moduleName);
1840 | }
1841 | };
1842 |
1843 | function getInteractiveScript() {
1844 | if (interactiveScript && interactiveScript.readyState === 'interactive') {
1845 | return interactiveScript;
1846 | }
1847 |
1848 | eachReverse(scripts(), function (script) {
1849 | if (script.readyState === 'interactive') {
1850 | return (interactiveScript = script);
1851 | }
1852 | });
1853 | return interactiveScript;
1854 | }
1855 |
1856 | //Look for a data-main script attribute, which could also adjust the baseUrl.
1857 | if (isBrowser) {
1858 | //Figure out baseUrl. Get it from the script tag with require.js in it.
1859 | eachReverse(scripts(), function (script) {
1860 | //Set the 'head' where we can append children by
1861 | //using the script's parent.
1862 | if (!head) {
1863 | head = script.parentNode;
1864 | }
1865 |
1866 | //Look for a data-main attribute to set main script for the page
1867 | //to load. If it is there, the path to data main becomes the
1868 | //baseUrl, if it is not already set.
1869 | dataMain = script.getAttribute('data-main');
1870 | if (dataMain) {
1871 | //Set final baseUrl if there is not already an explicit one.
1872 | if (!cfg.baseUrl) {
1873 | //Pull off the directory of data-main for use as the
1874 | //baseUrl.
1875 | src = dataMain.split('/');
1876 | mainScript = src.pop();
1877 | subPath = src.length ? src.join('/') + '/' : './';
1878 |
1879 | cfg.baseUrl = subPath;
1880 | dataMain = mainScript;
1881 | }
1882 |
1883 | //Strip off any trailing .js since dataMain is now
1884 | //like a module name.
1885 | dataMain = dataMain.replace(jsSuffixRegExp, '');
1886 |
1887 | //Put the data-main script in the files to load.
1888 | cfg.deps = cfg.deps ? cfg.deps.concat(dataMain) : [dataMain];
1889 |
1890 | return true;
1891 | }
1892 | });
1893 | }
1894 |
1895 | /**
1896 | * The function that handles definitions of modules. Differs from
1897 | * require() in that a string for the module should be the first argument,
1898 | * and the function to execute after dependencies are loaded should
1899 | * return a value to define the module corresponding to the first argument's
1900 | * name.
1901 | */
1902 | define = function (name, deps, callback) {
1903 | var node, context;
1904 |
1905 | //Allow for anonymous modules
1906 | if (typeof name !== 'string') {
1907 | //Adjust args appropriately
1908 | callback = deps;
1909 | deps = name;
1910 | name = null;
1911 | }
1912 |
1913 | //This module may not have dependencies
1914 | if (!isArray(deps)) {
1915 | callback = deps;
1916 | deps = [];
1917 | }
1918 |
1919 | //If no name, and callback is a function, then figure out if it a
1920 | //CommonJS thing with dependencies.
1921 | if (!deps.length && isFunction(callback)) {
1922 | //Remove comments from the callback string,
1923 | //look for require calls, and pull them into the dependencies,
1924 | //but only if there are function args.
1925 | if (callback.length) {
1926 | callback
1927 | .toString()
1928 | .replace(commentRegExp, '')
1929 | .replace(cjsRequireRegExp, function (match, dep) {
1930 | deps.push(dep);
1931 | });
1932 |
1933 | //May be a CommonJS thing even without require calls, but still
1934 | //could use exports, and module. Avoid doing exports and module
1935 | //work though if it just needs require.
1936 | //REQUIRES the function to expect the CommonJS variables in the
1937 | //order listed below.
1938 | deps = (callback.length === 1 ? ['require'] : ['require', 'exports', 'module']).concat(deps);
1939 | }
1940 | }
1941 |
1942 | //If in IE 6-8 and hit an anonymous define() call, do the interactive
1943 | //work.
1944 | if (useInteractive) {
1945 | node = currentlyAddingScript || getInteractiveScript();
1946 | if (node) {
1947 | if (!name) {
1948 | name = node.getAttribute('data-requiremodule');
1949 | }
1950 | context = contexts[node.getAttribute('data-requirecontext')];
1951 | }
1952 | }
1953 |
1954 | //Always save off evaluating the def call until the script onload handler.
1955 | //This allows multiple modules to be in a file without prematurely
1956 | //tracing dependencies, and allows for anonymous module support,
1957 | //where the module name is not known until the script onload event
1958 | //occurs. If no context, use the global queue, and get it processed
1959 | //in the onscript load callback.
1960 | (context ? context.defQueue : globalDefQueue).push([name, deps, callback]);
1961 | };
1962 |
1963 | define.amd = {
1964 | jQuery: true
1965 | };
1966 |
1967 |
1968 | /**
1969 | * Executes the text. Normally just uses eval, but can be modified
1970 | * to use a better, environment-specific call. Only used for transpiling
1971 | * loader plugins, not for plain JS modules.
1972 | * @param {String} text the text to execute/evaluate.
1973 | */
1974 | req.exec = function (text) {
1975 | /*jslint evil: true */
1976 | return eval(text);
1977 | };
1978 |
1979 | //Set up with config info.
1980 | req(cfg);
1981 | }(this));
--------------------------------------------------------------------------------
/js/lib/trie/base64.js:
--------------------------------------------------------------------------------
1 | define([], function () {
2 |
3 | var charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
4 |
5 | var charWidth = 6;
6 |
7 | var bitsInByte = [
8 | 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, 2, 2, 3, 2, 3, 3, 4, 2,
9 | 3, 3, 4, 3, 4, 4, 5, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3,
10 | 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3,
11 | 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4,
12 | 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5,
13 | 6, 6, 7, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4,
14 | 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5,
15 | 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 2, 3, 3, 4, 3, 4, 4, 5,
16 | 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 3,
17 | 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6,
18 | 6, 7, 6, 7, 7, 8
19 | ];
20 |
21 | function chr(index) {
22 | return charset[index];
23 | }
24 |
25 | function width() {
26 | return charWidth;
27 | }
28 |
29 |
30 | var cache = {
31 | "A": 0, "B": 1, "C": 2, "D": 3, "E": 4, "F": 5, "G": 6, "H": 7,
32 | "I": 8, "J": 9, "K": 10, "L": 11, "M": 12, "N": 13, "O": 14, "P": 15,
33 | "Q": 16, "R": 17, "S": 18, "T": 19, "U": 20, "V": 21, "W": 22, "X": 23,
34 | "Y": 24, "Z": 25, "a": 26, "b": 27, "c": 28, "d": 29, "e": 30, "f": 31,
35 | "g": 32, "h": 33, "i": 34, "j": 35, "k": 36, "l": 37, "m": 38, "n": 39,
36 | "o": 40, "p": 41, "q": 42, "r": 43, "s": 44, "t": 45, "u": 46, "v": 47,
37 | "w": 48, "x": 49, "y": 50, "z": 51, "0": 52, "1": 53, "2": 54, "3": 55,
38 | "4": 56, "5": 57, "6": 58, "7": 59, "8": 60, "9": 61, "-": 62, "_": 63
39 | };
40 |
41 | // The integral value for a Base64 character
42 | function index(ch) {
43 | return cache[ch];
44 | }
45 |
46 | // The number of bits in a Base64 character
47 | function bits(ch) {
48 | return bitsInByte[index(ch)];
49 | }
50 |
51 | function c2i(s, offset) {
52 | var c, cVal, val = 0;
53 | for (var i = 0; i < 4; i++) {
54 | c = s.charAt(offset + i);
55 | cVal = index(c);
56 | val = (val << 6) | cVal;
57 | }
58 | return val;
59 | }
60 |
61 | function i2c(val) {
62 | var ONES = 0x3f,
63 | WIDTH = 6,
64 | out = "";
65 |
66 | out += charset.charAt(((ONES << (WIDTH*3)) & val)>>>(WIDTH*3));
67 | out += charset.charAt(((ONES << (WIDTH*2)) & val)>>>(WIDTH*2));
68 | out += charset.charAt(((ONES << WIDTH) & val)>>>WIDTH);
69 | out += charset.charAt(ONES & val);
70 | return out;
71 | }
72 |
73 | return {
74 | charWidth: charWidth,
75 | width: width,
76 | chr: chr,
77 | index: index,
78 | bits: bits,
79 | c2i: c2i,
80 | i2c: i2c
81 | }
82 | });
--------------------------------------------------------------------------------
/js/lib/trie/bitstring.js:
--------------------------------------------------------------------------------
1 | define(["trie/base64"], function (Base64) {
2 |
3 | var W = Base64.charWidth,
4 | maskTop = [
5 | 0x3f, 0x1f, 0x0f, 0x07, 0x03, 0x01, 0x00
6 | ],
7 | bitsInByte = [
8 | 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, 2, 2, 3, 2, 3, 3, 4, 2,
9 | 3, 3, 4, 3, 4, 4, 5, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3,
10 | 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3,
11 | 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4,
12 | 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5,
13 | 6, 6, 7, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4,
14 | 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5,
15 | 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 2, 3, 3, 4, 3, 4, 4, 5,
16 | 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 3,
17 | 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6,
18 | 6, 7, 6, 7, 7, 8
19 | ];
20 |
21 | function BitString(str) {
22 | this.bytes = str;
23 | this.length = this.bytes.length * W;
24 | }
25 |
26 | BitString.prototype.getData = function () {
27 | return this.bytes;
28 | }
29 |
30 | /**
31 | Returns a decimal number, consisting of a certain number, nBits, of bits
32 | starting at a certain position, position.
33 | */
34 | BitString.prototype.get = function (position, nBits) {
35 |
36 | // case 1: bits lie within the given byte
37 | if (( position % W ) + nBits <= W) {
38 | return (Base64.index(this.bytes[ position / W | 0 ]) & maskTop[ position % W ] ) >> ( W - position % W - nBits );
39 |
40 | // case 2: bits lie incompletely in the given byte
41 | } else {
42 | var result = ( Base64.index(this.bytes[ position / W | 0 ]) & maskTop[ position % W ] ), l = W - position % W;
43 |
44 | position += l;
45 | nBits -= l;
46 |
47 | while (nBits >= W) {
48 | result = (result << W) | Base64.index(this.bytes[ position / W | 0 ]);
49 | position += W;
50 | nBits -= W;
51 | }
52 |
53 | if (nBits > 0) {
54 | result = (result << nBits) | ( Base64.index(this.bytes[ position / W | 0 ]) >> ( W - nBits ) );
55 | }
56 | return result;
57 | }
58 | }
59 |
60 | /**
61 | Counts the number of bits set to 1 starting at position p and
62 | ending at position p + n
63 | */
64 | BitString.prototype.count = function (p, n) {
65 |
66 | var count = 0;
67 | while (n >= 8) {
68 | count += bitsInByte[ this.get(p, 8) ];
69 | p += 8;
70 | n -= 8;
71 | }
72 | return count + bitsInByte[ this.get(p, n) ];
73 | }
74 |
75 | /**
76 | Returns the number of bits set to 1 up to and including position x.
77 | This is the slow implementation used for testing.
78 | */
79 | BitString.prototype.rank = function (x) {
80 | var rank = 0,
81 | i;
82 | for (i = 0; i <= x; i++) {
83 | if (this.get(i, 1)) {
84 | rank++;
85 | }
86 | }
87 | return rank;
88 | }
89 |
90 | return BitString;
91 | });
92 |
--------------------------------------------------------------------------------
/js/lib/trie/bitwriter.js:
--------------------------------------------------------------------------------
1 | define(["trie/base64"], function (Base64) {
2 |
3 | function BitWriter() {
4 | this.bits = [];
5 | }
6 |
7 | // numBits <= 32 as data is a 32-bit int.
8 | BitWriter.prototype.write = function (data, numBits) {
9 | for (var i = numBits - 1; i >= 0; i--) {
10 | if (data & ( 1 << i )) {
11 | this.bits.push(1);
12 | } else {
13 | this.bits.push(0);
14 | }
15 | }
16 | };
17 |
18 | BitWriter.prototype.getData = function () {
19 | var chars = [],
20 | b = 0,
21 | i = 0,
22 | j;
23 |
24 | for (j = 0; j < this.bits.length; j++) {
25 | b = (b << 1) | this.bits[j];
26 | i += 1;
27 | if (i === Base64.charWidth) {
28 | chars.push(Base64.chr(b));
29 | i = b = 0;
30 | }
31 | }
32 |
33 | if (i > 0) {
34 | chars.push(Base64.chr(b << ( Base64.charWidth - i )));
35 | }
36 | return chars.join("");
37 | }
38 |
39 | return BitWriter;
40 | });
41 |
--------------------------------------------------------------------------------
/js/lib/trie/compressedtrie.js:
--------------------------------------------------------------------------------
1 | define(["trie/compressedtrienode", "trie/rankdirectory", "trie/simpleselectindex", "trie/bitstring"],
2 |
3 | function (CompressedTrieNode, RankDirectory, SimpleSelectIndex, BitString) {
4 |
5 | function CompressedTrie(data, directoryData, nodeCount) {
6 | this.init(data, directoryData, nodeCount);
7 | }
8 |
9 | CompressedTrie.prototype.init = function (data, nodeCount, directoryData) {
10 | this.data = new BitString(data);
11 |
12 |
13 | // The position of the first bit of the data in 0th node. In non-root
14 | // nodes, this would contain 6-bit letters.
15 | this.letterStart = nodeCount * 2 + 1;
16 |
17 | if (directoryData) {
18 | this.simpleSelect = new SimpleSelectIndex(data).load(directoryData);
19 | } else {
20 | this.simpleSelect = new SimpleSelectIndex(data).create(this.letterStart);
21 | }
22 | }
23 |
24 | /**
25 | Retrieve the CompressedTrieNode of the trie, given its index in level-order.
26 | This is a private function that you don't have to use.
27 | */
28 | CompressedTrie.prototype.getNodeByIndex = function (index) {
29 | // retrieve the 6-bit letter.
30 | var final = this.data.get(this.letterStart + index * 6, 1) === 1,
31 | letter = String.fromCharCode(this.data.get(this.letterStart + index * 6 + 1, 5) + 'a'.charCodeAt(0)),
32 | firstChildC = this.simpleSelect.select(index + 1) - index;
33 |
34 | // Since the nodes are in level order, this nodes children must go up
35 | // until the next node's children start.
36 | var childOfNextNodeC = this.simpleSelect.select(index+2) -index -1;
37 |
38 | return new CompressedTrieNode(this, index, letter, final, firstChildC,
39 | childOfNextNodeC - firstChildC);
40 | }
41 |
42 | /**
43 | Retrieve the root node. You can use this node to obtain all of the other
44 | nodes in the trie.
45 | */
46 | CompressedTrie.prototype.getRoot = function () {
47 | return this.getNodeByIndex(0);
48 | }
49 |
50 | /**
51 | Look-up a word in the trie. Returns true if and only if the word exists
52 | in the trie.
53 | */
54 | CompressedTrie.prototype.lookup = function (word) {
55 | var j, child, childCount, node = this.getRoot();
56 | for (var i = 0; i < word.length; i++) {
57 | j = 0;
58 | childCount = node.getChildCount();
59 | for (; j < childCount; j++) {
60 | child = node.getChild(j);
61 | if (child.letter === word.charAt(i)) {
62 | break;
63 | }
64 | }
65 |
66 | if (j === childCount) {
67 | return false;
68 | }
69 | node = child;
70 | }
71 | return node.final;
72 | }
73 |
74 | CompressedTrie.prototype.toJSON = function() {
75 | return {
76 | trie: this.data.getData(),
77 | simpleDirectory: this.simpleSelect.getDirectory(),
78 | nodeCount: (this.letterStart - 1)/2
79 | }
80 | }
81 |
82 | return CompressedTrie;
83 | });
--------------------------------------------------------------------------------
/js/lib/trie/compressedtrienode.js:
--------------------------------------------------------------------------------
1 | define([], function () {
2 |
3 | function CompressedTrieNode(trie, index, letter, final, firstChild, childCount) {
4 | this.trie = trie;
5 | this.index = index;
6 | this.letter = letter;
7 | this.final = final;
8 | this.firstChild = firstChild;
9 | this.childCount = childCount;
10 | }
11 |
12 | CompressedTrieNode.prototype.getChildCount = function () {
13 | return this.childCount;
14 | }
15 |
16 | CompressedTrieNode.prototype.getChild = function (index) {
17 | return this.trie.getNodeByIndex(this.firstChild + index);
18 | }
19 |
20 | CompressedTrieNode.prototype.find = function(letter) {
21 | var j = 0,
22 | child;
23 |
24 | for (; j < this.getChildCount(); j++) {
25 | child = this.getChild(j);
26 | if (child.letter === letter) {
27 | return child;
28 | }
29 | }
30 | return null;
31 | }
32 |
33 | return CompressedTrieNode;
34 |
35 | });
--------------------------------------------------------------------------------
/js/lib/trie/rankdirectory.js:
--------------------------------------------------------------------------------
1 | define(["trie/bitstring", "trie/bitwriter"], function (BitString, BitWriter) {
2 |
3 |
4 | var l1Size = 32 * 32,
5 | l2Size = 32;
6 |
7 | function RankDirectory(directoryData, bitData, numBits) {
8 | this.init(directoryData, bitData, numBits, l1Size, l2Size);
9 | }
10 |
11 | /**
12 | Used to build a rank directory from the given input string.
13 |
14 | @param data A javascript string containing the data, as readable using the
15 | BitString object.
16 |
17 | @param numBits The number of bits to index.
18 |
19 | @param l1Size The number of bits that each entry in the Level 1 table
20 | summarizes. This should be a multiple of l2Size.
21 |
22 | @param l2Size The number of bits that each entry in the Level 2 table
23 | summarizes.
24 | */
25 | RankDirectory.Create = function (data, numBits) {
26 | var bits = new BitString(data);
27 | var p = 0;
28 | var i = 0;
29 | var count1 = 0, count2 = 0;
30 | var l1bits = Math.ceil(Math.log(numBits) / Math.log(2));
31 | var l2bits = Math.ceil(Math.log(l1Size) / Math.log(2));
32 |
33 | var directory = new BitWriter();
34 |
35 | while (p + l2Size <= numBits) {
36 | count2 += bits.count(p, l2Size);
37 | i += l2Size;
38 | p += l2Size;
39 | if (i === l1Size) {
40 | count1 += count2;
41 | directory.write(count1, l1bits);
42 | count2 = 0;
43 | i = 0;
44 | } else {
45 | directory.write(count2, l2bits);
46 | }
47 | }
48 |
49 | return new RankDirectory(directory.getData(), data, numBits);
50 | };
51 |
52 |
53 | RankDirectory.prototype.init = function (directoryData, bitData, numBits, l1Size, l2Size) {
54 | this.directory = new BitString(directoryData);
55 | this.data = new BitString(bitData);
56 | this.l1Size = l1Size;
57 | this.l2Size = l2Size;
58 | this.l1Bits = Math.ceil(Math.log(numBits) / Math.log(2));
59 | this.l2Bits = Math.ceil(Math.log(l1Size) / Math.log(2));
60 | this.sectionBits = (l1Size / l2Size - 1) * this.l2Bits + this.l1Bits;
61 | this.numBits = numBits;
62 | }
63 |
64 | /**
65 | Returns the string representation of the directory.
66 | */
67 | RankDirectory.prototype.getData = function () {
68 | return this.directory.getData();
69 | }
70 |
71 | /**
72 | Returns the number of 1 or 0 bits (depending on the "which" parameter) to
73 | to and including position x.
74 | */
75 | RankDirectory.prototype.rank = function (which, x) {
76 | var rank = 0,
77 | o = x,
78 | sectionPos = 0;
79 |
80 | if (which === 0) {
81 | return x - this.rank(1, x) + 1;
82 | }
83 |
84 |
85 | if (o >= this.l1Size) {
86 | sectionPos = ( o / this.l1Size | 0 ) * this.sectionBits;
87 | rank = this.directory.get(sectionPos - this.l1Bits, this.l1Bits);
88 | o = o % this.l1Size;
89 | }
90 |
91 | if (o >= this.l2Size) {
92 | sectionPos += ( o / this.l2Size | 0 ) * this.l2Bits;
93 | rank += this.directory.get(sectionPos - this.l2Bits, this.l2Bits);
94 | }
95 |
96 | rank += this.data.count(x - x % this.l2Size, x % this.l2Size + 1);
97 |
98 | return rank;
99 | }
100 |
101 | /**
102 | Returns the position of the y'th 0 or 1 bit, depending on the "which"
103 | parameter.
104 | */
105 | RankDirectory.prototype.select = function (which, y) {
106 | var high = this.numBits,
107 | low = -1,
108 | val = -1;
109 |
110 | while (high - low > 1) {
111 | var probe = (high + low) / 2 | 0;
112 | var r = this.rank(which, probe);
113 |
114 | if (r === y) {
115 | // We have to continue searching after we have found it,
116 | // because we want the _first_ occurrence.
117 | val = probe;
118 | high = probe;
119 | } else if (r < y) {
120 | low = probe;
121 | } else {
122 | high = probe;
123 | }
124 | }
125 | return val;
126 | }
127 |
128 | return RankDirectory;
129 | });
--------------------------------------------------------------------------------
/js/lib/trie/simpleselectindex.js:
--------------------------------------------------------------------------------
1 | define(["trie/base64", "trie/stringintarray"], function(Base64, StringIntArray) {
2 |
3 | var START_VAL = -1,
4 | CHUNK_SIZE = 32,
5 | WIDTH = Base64.width();
6 |
7 | function intDiv(a,b) {
8 | var result = a/b;
9 | return (result>=0) ?Math.floor(result) : Math.ceil(result);
10 | }
11 |
12 | function SimpleSelectIndex(data) {
13 | this.data = data;
14 | this.directory = new StringIntArray();
15 | }
16 |
17 | SimpleSelectIndex.prototype.create = function(nBits) {
18 | var nChars = 1 + intDiv(nBits, WIDTH);
19 | this.init((nChars < this.data.length) ? nChars : this.data.length);
20 | return this;
21 | }
22 |
23 | SimpleSelectIndex.prototype.load = function(directoryData) {
24 | this.directory.set(directoryData);
25 | return this;
26 | }
27 |
28 | SimpleSelectIndex.prototype.getData = function() {
29 | return this.data;
30 | }
31 |
32 | SimpleSelectIndex.prototype.getDirectory= function() {
33 | return this.directory.toString();
34 | }
35 |
36 | SimpleSelectIndex.prototype.size = function() {
37 | return this.directory.size();
38 | }
39 |
40 | SimpleSelectIndex.prototype.select = function(index) {
41 | var state = new State(index, this.data, this.directory);
42 | while (state.getCount() < index) {
43 | state.step();
44 | }
45 | return state.getIndex();
46 |
47 | }
48 |
49 |
50 | SimpleSelectIndex.prototype.init = function(nChars) {
51 | var curCount = 0,
52 | index = 0,
53 | totalZero = 0,
54 | totalBits = 0,
55 | c, nZeroBits, nextCount, nBitsToComplete, charIndex;
56 | for (var i = 0; i < nChars; i++) {
57 | c = this.data.charAt(i);
58 | nZeroBits = WIDTH - Base64.bits(c);
59 | totalZero += nZeroBits;
60 | totalBits += WIDTH;
61 | nextCount = curCount + nZeroBits;
62 | if (nextCount < CHUNK_SIZE) {
63 | index = totalBits;
64 | curCount = nextCount;
65 | } else {
66 | //assert nextCount >= CHUNK_SIZE;
67 | nBitsToComplete = CHUNK_SIZE - curCount;
68 | charIndex = this.findZeroBit(nBitsToComplete, c);
69 | index += charIndex;
70 | this.directory.add(index);
71 | curCount = nZeroBits - nBitsToComplete;
72 | index = totalBits;
73 | }
74 | }
75 | }
76 |
77 | SimpleSelectIndex.prototype.findZeroBit = function(i, c) {
78 | var toFind, cVal, val;
79 |
80 | if (i == 0) {
81 | return 0;
82 | }
83 | toFind = i;
84 | cVal = Base64.index(c);
85 | for (var j = WIDTH - 1; j >= 0; j--) {
86 | val = (1<= directory.size()) {
115 | LOG.debug("oops");
116 | }
117 | startCount = (chunk == START_VAL) ? 0 : (1 + chunk) * CHUNK_SIZE;
118 | startPosition = (chunk == START_VAL) ? -1 : directory.get(chunk);
119 |
120 | this.currentCount = startCount;
121 |
122 | // currentChar/currentOffset point to next bit beyond currentCount except when targetCount is reached
123 | this.currentChar = (startPosition == START_VAL) ? 0 : intDiv(startPosition,WIDTH);
124 | this.currentOffset = (startPosition == START_VAL) ? -1 : startPosition % WIDTH;
125 | if (this.currentCount < this.targetCount) {
126 | this.advance();
127 | }
128 | }
129 |
130 | State.prototype.advance = function() {
131 | this.currentOffset++;
132 | if (this.currentOffset > 0 && this.currentOffset % WIDTH == 0) {
133 | this.currentChar++;
134 | this.currentOffset = 0;
135 | }
136 | }
137 | State.prototype.getIndex = function() {
138 | return (this.currentCount == 0) ? -1 : this.currentChar * WIDTH +this. currentOffset;
139 | }
140 |
141 | State.prototype.getCount = function() {
142 | return this.currentCount;
143 | }
144 |
145 | State.prototype.value = function() {
146 | var value = Base64.index(this.data.charAt(this.currentChar)),
147 | offset = WIDTH-1-this.currentOffset;
148 | return (1< 0) {
154 | val = this.value();
155 | if (val == 0) {
156 | this.currentCount++;
157 | }
158 | if (this.currentCount < this.targetCount) {
159 | this.advance();
160 | }
161 | } else {
162 | bits = WIDTH - Base64.bits(this.data.charAt(this.currentChar));
163 | if (this.currentCount + bits < this.targetCount) {
164 | this.currentCount += bits;
165 | this.currentChar++;
166 | this.currentOffset = 0;
167 | } else {
168 | val = this.value();
169 | if (val == 0) {
170 | this.currentCount++;
171 | }
172 | if (this.currentCount < this.targetCount) {
173 | this.currentOffset++;
174 | }
175 | }
176 | }
177 | return this.currentCount;
178 | }
179 |
180 | return SimpleSelectIndex;
181 |
182 | });
--------------------------------------------------------------------------------
/js/lib/trie/stringintarray.js:
--------------------------------------------------------------------------------
1 | define(["trie/base64"], function(Base64) {
2 |
3 | function StringIntArray() {
4 | this.builder = "";
5 | }
6 |
7 | StringIntArray.prototype.set = function(s) {
8 | this.builder = s;
9 | return this;
10 | }
11 |
12 | StringIntArray.prototype.add = function(val) {
13 | this.builder = this.builder + Base64.i2c(val);
14 | }
15 |
16 | StringIntArray.prototype.size = function() {
17 | return this.builder.length /4;
18 | }
19 |
20 | StringIntArray.prototype.get = function(index) {
21 | return Base64.c2i(this.builder, index*4);
22 | }
23 |
24 | StringIntArray.prototype.toString = function() {
25 | return this.builder;
26 | }
27 |
28 | return StringIntArray;
29 | });
--------------------------------------------------------------------------------
/js/lib/trie/trie.js:
--------------------------------------------------------------------------------
1 | define(["trie/bitwriter"], function(BitWriter) {
2 |
3 | function TrieNode(letter) {
4 | this.letter = letter;
5 | this.final = false;
6 | this.children = [];
7 | }
8 |
9 | function Trie() {
10 | this.init();
11 | }
12 |
13 | Trie.prototype.init = function() {
14 | this.previousWord = "";
15 | this.root = new TrieNode(' ');
16 | this.cache = [this.root];
17 | this.nodeCount = 1;
18 | }
19 |
20 | Trie.prototype.getNodeCount = function() {
21 | return this.nodeCount;
22 | }
23 |
24 | Trie.prototype.insert = function(word) {
25 | var commonPrefix = 0;
26 | for (var i = 0; i < Math.min(word.length, this.previousWord.length);
27 | i++) {
28 | if (word[i] !== this.previousWord[i]) {
29 | break;
30 | }
31 | commonPrefix += 1;
32 | }
33 |
34 | this.cache.length = commonPrefix + 1;
35 | var node = this.cache[this.cache.length - 1];
36 |
37 | for (i = commonPrefix; i < word.length; i++) {
38 | var next = new TrieNode(word[i]);
39 | this.nodeCount++;
40 | node.children.push(next);
41 | this.cache.push(next);
42 | node = next;
43 | }
44 |
45 | node.final = true;
46 | this.previousWord = word;
47 | }
48 |
49 | Trie.prototype.apply = function(fn) {
50 | var level = [this.root];
51 | while (level.length > 0) {
52 | var node = level.shift();
53 | for (var i = 0; i < node.children.length; i++) {
54 | level.push(node.children[i]);
55 | }
56 | fn(node);
57 | }
58 | }
59 |
60 | Trie.prototype.encode = function() {
61 | var bits = new BitWriter();
62 | bits.write(0x02, 2);
63 | this.apply(function(node) {
64 | for (var i = 0; i < node.children.length; i++) {
65 | bits.write(1, 1);
66 | }
67 | bits.write(0, 1);
68 | });
69 |
70 | var a = ("a").charCodeAt(0);
71 | this.apply(function(node) {
72 | var value = node.letter.charCodeAt(0) - a;
73 | if (node.final) {
74 | value |= 0x20;
75 | }
76 |
77 | bits.write(value, 6);
78 | });
79 |
80 | return bits.getData();
81 | }
82 |
83 | return Trie;
84 |
85 | });
86 |
--------------------------------------------------------------------------------