1 /**
  2  * UUID.js: The RFC-compliant UUID generator for JavaScript.
  3  *
  4  * @fileOverview
  5  * @author  LiosK
  6  * @version 3.2
  7  * @license The MIT License: Copyright (c) 2010-2012 LiosK.
  8  */
  9 
 10 /** @constructor */
 11 var UUID;
 12 
 13 UUID = (function(overwrittenUUID) {
 14 
 15 // Core Component {{{
 16 
 17 /** @lends UUID */
 18 function UUID() {}
 19 
 20 /**
 21  * The simplest function to get an UUID string.
 22  * @returns {string} A version 4 UUID string.
 23  */
 24 UUID.generate = function() {
 25   var rand = UUID._getRandomInt, hex = UUID._hexAligner;
 26   return  hex(rand(32), 8)          // time_low
 27         + "-"
 28         + hex(rand(16), 4)          // time_mid
 29         + "-"
 30         + hex(0x4000 | rand(12), 4) // time_hi_and_version
 31         + "-"
 32         + hex(0x8000 | rand(14), 4) // clock_seq_hi_and_reserved clock_seq_low
 33         + "-"
 34         + hex(rand(48), 12);        // node
 35 };
 36 
 37 /**
 38  * Returns an unsigned x-bit random integer.
 39  * @param {int} x A positive integer ranging from 0 to 53, inclusive.
 40  * @returns {int} An unsigned x-bit random integer (0 <= f(x) < 2^x).
 41  */
 42 UUID._getRandomInt = function(x) {
 43   if (x <   0) return NaN;
 44   if (x <= 30) return (0 | Math.random() * (1 <<      x));
 45   if (x <= 53) return (0 | Math.random() * (1 <<     30))
 46                     + (0 | Math.random() * (1 << x - 30)) * (1 << 30);
 47   return NaN;
 48 };
 49 
 50 /**
 51  * Returns a function that converts an integer to a zero-filled string.
 52  * @param {int} radix
 53  * @returns {function(num, length)}
 54  */
 55 UUID._getIntAligner = function(radix) {
 56   return function(num, length) {
 57     var str = num.toString(radix), i = length - str.length, z = "0";
 58     for (; i > 0; i >>>= 1, z += z) { if (i & 1) { str = z + str; } }
 59     return str;
 60   };
 61 };
 62 
 63 UUID._hexAligner = UUID._getIntAligner(16);
 64 
 65 // }}}
 66 
 67 // UUID Object Component {{{
 68 
 69 /**
 70  * Names of each UUID field.
 71  * @type string[]
 72  * @constant
 73  * @since 3.0
 74  */
 75 UUID.FIELD_NAMES = ["timeLow", "timeMid", "timeHiAndVersion",
 76                     "clockSeqHiAndReserved", "clockSeqLow", "node"];
 77 
 78 /**
 79  * Sizes of each UUID field.
 80  * @type int[]
 81  * @constant
 82  * @since 3.0
 83  */
 84 UUID.FIELD_SIZES = [32, 16, 16, 8, 8, 48];
 85 
 86 /**
 87  * Generates a version 4 {@link UUID}.
 88  * @returns {UUID} A version 4 {@link UUID} object.
 89  * @since 3.0
 90  */
 91 UUID.genV4 = function() {
 92   var rand = UUID._getRandomInt;
 93   return new UUID()._init(rand(32), rand(16), // time_low time_mid
 94                           0x4000 | rand(12),  // time_hi_and_version
 95                           0x80   | rand(6),   // clock_seq_hi_and_reserved
 96                           rand(8), rand(48)); // clock_seq_low node
 97 };
 98 
 99 /**
100  * Converts hexadecimal UUID string to an {@link UUID} object.
101  * @param {string} strId UUID hexadecimal string representation ("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx").
102  * @returns {UUID} {@link UUID} object or null.
103  * @since 3.0
104  */
105 UUID.parse = function(strId) {
106   var r, p = /^\s*(urn:uuid:|\{)?([0-9a-f]{8})-([0-9a-f]{4})-([0-9a-f]{4})-([0-9a-f]{2})([0-9a-f]{2})-([0-9a-f]{12})(\})?\s*$/i;
107   if (r = p.exec(strId)) {
108     var l = r[1] || "", t = r[8] || "";
109     if (((l + t) === "") ||
110         (l === "{" && t === "}") ||
111         (l.toLowerCase() === "urn:uuid:" && t === "")) {
112       return new UUID()._init(parseInt(r[2], 16), parseInt(r[3], 16),
113                               parseInt(r[4], 16), parseInt(r[5], 16),
114                               parseInt(r[6], 16), parseInt(r[7], 16));
115     }
116   }
117   return null;
118 };
119 
120 /**
121  * Initializes {@link UUID} object.
122  * @param {uint32} [timeLow=0] time_low field (octet 0-3).
123  * @param {uint16} [timeMid=0] time_mid field (octet 4-5).
124  * @param {uint16} [timeHiAndVersion=0] time_hi_and_version field (octet 6-7).
125  * @param {uint8} [clockSeqHiAndReserved=0] clock_seq_hi_and_reserved field (octet 8).
126  * @param {uint8} [clockSeqLow=0] clock_seq_low field (octet 9).
127  * @param {uint48} [node=0] node field (octet 10-15).
128  * @returns {UUID} this.
129  */
130 UUID.prototype._init = function() {
131   var names = UUID.FIELD_NAMES, sizes = UUID.FIELD_SIZES;
132   var bin = UUID._binAligner, hex = UUID._hexAligner;
133 
134   /**
135    * List of UUID field values (as integer values).
136    * @type int[]
137    */
138   this.intFields = new Array(6);
139 
140   /**
141    * List of UUID field values (as binary bit string values).
142    * @type string[]
143    */
144   this.bitFields = new Array(6);
145 
146   /**
147    * List of UUID field values (as hexadecimal string values).
148    * @type string[]
149    */
150   this.hexFields = new Array(6);
151 
152   for (var i = 0; i < 6; i++) {
153     var intValue = parseInt(arguments[i] || 0);
154     this.intFields[i] = this.intFields[names[i]] = intValue;
155     this.bitFields[i] = this.bitFields[names[i]] = bin(intValue, sizes[i]);
156     this.hexFields[i] = this.hexFields[names[i]] = hex(intValue, sizes[i] / 4);
157   }
158 
159   /**
160    * UUID version number defined in RFC 4122.
161    * @type int
162    */
163   this.version = (this.intFields.timeHiAndVersion >> 12) & 0xF;
164 
165   /**
166    * 128-bit binary bit string representation.
167    * @type string
168    */
169   this.bitString = this.bitFields.join("");
170 
171   /**
172    * UUID hexadecimal string representation ("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx").
173    * @type string
174    */
175   this.hexString = this.hexFields[0] + "-" + this.hexFields[1] + "-" + this.hexFields[2]
176                  + "-" + this.hexFields[3] + this.hexFields[4] + "-" + this.hexFields[5];
177 
178   /**
179    * UUID string representation as a URN ("urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx").
180    * @type string
181    */
182   this.urn = "urn:uuid:" + this.hexString;
183 
184   return this;
185 };
186 
187 UUID._binAligner = UUID._getIntAligner(2);
188 
189 /**
190  * Returns UUID string representation.
191  * @returns {string} {@link UUID#hexString}.
192  */
193 UUID.prototype.toString = function() { return this.hexString; };
194 
195 /**
196  * Tests if two {@link UUID} objects are equal.
197  * @param {UUID} uuid
198  * @returns {bool} True if two {@link UUID} objects are equal.
199  */
200 UUID.prototype.equals = function(uuid) {
201   if (!(uuid instanceof UUID)) { return false; }
202   for (var i = 0; i < 6; i++) {
203     if (this.intFields[i] !== uuid.intFields[i]) { return false; }
204   }
205   return true;
206 };
207 
208 // }}}
209 
210 // UUID Version 1 Component {{{
211 
212 /**
213  * Generates a version 1 {@link UUID}.
214  * @returns {UUID} A version 1 {@link UUID} object.
215  * @since 3.0
216  */
217 UUID.genV1 = function() {
218   var now = new Date().getTime(), st = UUID._state;
219   if (now != st.timestamp) {
220     if (now < st.timestamp) { st.sequence++; }
221     st.timestamp = now;
222     st.tick = UUID._getRandomInt(4);
223   } else if (Math.random() < UUID._tsRatio && st.tick < 9984) {
224     // advance the timestamp fraction at a probability
225     // to compensate for the low timestamp resolution
226     st.tick += 1 + UUID._getRandomInt(4);
227   } else {
228     st.sequence++;
229   }
230 
231   // format time fields
232   var tf = UUID._getTimeFieldValues(st.timestamp);
233   var tl = tf.low + st.tick;
234   var thav = (tf.hi & 0xFFF) | 0x1000;  // set version '0001'
235 
236   // format clock sequence
237   st.sequence &= 0x3FFF;
238   var cshar = (st.sequence >>> 8) | 0x80; // set variant '10'
239   var csl = st.sequence & 0xFF;
240 
241   return new UUID()._init(tl, tf.mid, thav, cshar, csl, st.node);
242 };
243 
244 /**
245  * Re-initializes version 1 UUID state.
246  * @since 3.0
247  */
248 UUID.resetState = function() {
249   UUID._state = new UUID._state.constructor();
250 };
251 
252 /**
253  * Probability to advance the timestamp fraction: the ratio of tick movements to sequence increments.
254  * @type float
255  */
256 UUID._tsRatio = 1 / 4;
257 
258 /**
259  * Persistent state for UUID version 1.
260  * @type UUIDState
261  */
262 UUID._state = new function UUIDState() {
263   var rand = UUID._getRandomInt;
264   this.timestamp = 0;
265   this.sequence = rand(14);
266   this.node = (rand(8) | 1) * 0x10000000000 + rand(40); // set multicast bit '1'
267   this.tick = rand(4);  // timestamp fraction smaller than a millisecond
268 };
269 
270 /**
271  * @param {Date|int} time ECMAScript Date Object or milliseconds from 1970-01-01.
272  * @returns {object}
273  */
274 UUID._getTimeFieldValues = function(time) {
275   var ts = time - Date.UTC(1582, 9, 15);
276   var hm = ((ts / 0x100000000) * 10000) & 0xFFFFFFF;
277   return  { low: ((ts & 0xFFFFFFF) * 10000) % 0x100000000,
278             mid: hm & 0xFFFF, hi: hm >>> 16, timestamp: ts };
279 };
280 
281 // }}}
282 
283 // Misc. Component {{{
284 
285 /**
286  * Reinstalls {@link UUID.generate} method to emulate the interface of UUID.js version 2.x.
287  * @since 3.1
288  * @deprecated Version 2.x. compatible interface is not recommended.
289  */
290 UUID.makeBackwardCompatible = function() {
291   var f = UUID.generate;
292   UUID.generate = function(o) {
293     return (o && o.version == 1) ? UUID.genV1().hexString : f.call(UUID);
294   };
295   UUID.makeBackwardCompatible = function() {};
296 };
297 
298 /**
299  * Preserves the value of 'UUID' global variable set before the load of UUID.js.
300  * @since 3.2
301  * @type object
302  */
303 UUID.overwrittenUUID = overwrittenUUID;
304 
305 // }}}
306 
307 return UUID;
308 
309 })(UUID);
310 
311 // vim: et ts=2 sw=2 fdm=marker fmr&
312