diff --git a/.all-contributorsrc b/.all-contributorsrc index 55809f85bd..1afd5d8896 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1326,7 +1326,8 @@ "profile": "https://aatishb.com", "contributions": [ "doc", - "bug" + "bug", + "example" ] }, { @@ -1391,7 +1392,8 @@ "contributions": [ "bug", "code", - "doc" + "doc", + "example" ] }, { @@ -1400,7 +1402,8 @@ "avatar_url": "https://avatars2.githubusercontent.com/u/9020979?s=460&v=4", "profile": "https://github.com/hydrosquall", "contributions": [ - "doc" + "doc", + "example" ] }, { @@ -1851,7 +1854,8 @@ "avatar_url": "https://avatars1.githubusercontent.com/u/36653440?v=4", "profile": "https://github.com/sm7515", "contributions": [ - "doc" + "doc", + "example" ] }, { @@ -2174,7 +2178,8 @@ "avatar_url": "https://avatars1.githubusercontent.com/u/56646605?v=4", "profile": "https://berkeozgen.me/", "contributions": [ - "bug" + "bug", + "example" ] }, { @@ -2495,7 +2500,8 @@ "avatar_url": "https://avatars.githubusercontent.com/u/43292181?v=4", "profile": "https://github.com/suhascv", "contributions": [ - "doc" + "doc", + "example" ] }, { @@ -2555,7 +2561,8 @@ "profile": "http://www.davepagurek.com", "contributions": [ "code", - "test" + "test", + "example" ] }, { @@ -3323,7 +3330,8 @@ "profile": "https://github.com/Malayvasa", "contributions": [ "design", - "code" + "code", + "example" ] }, { @@ -3659,7 +3667,8 @@ "contributions": [ "ideas", "review", - "plugin" + "plugin", + "example" ] }, { @@ -3767,7 +3776,8 @@ "profile": "http://aceslowman.com", "contributions": [ "code", - "doc" + "doc", + "example" ] }, { @@ -3947,7 +3957,8 @@ "avatar_url": "https://avatars.githubusercontent.com/u/74553433?v=4", "profile": "https://github.com/Acha0203", "contributions": [ - "code" + "code", + "example" ] }, { @@ -4091,7 +4102,8 @@ "avatar_url": "https://avatars.githubusercontent.com/u/92529?v=4", "profile": "http://jareddonovan.com/", "contributions": [ - "code" + "code", + "example" ] }, { @@ -4460,7 +4472,8 @@ "avatar_url": "https://avatars.githubusercontent.com/u/22816171?v=4", "profile": "https://github.com/ihsavru", "contributions": [ - "code" + "code", + "example" ] }, { @@ -4523,7 +4536,8 @@ "avatar_url": "https://avatars.githubusercontent.com/u/19987520?v=4", "profile": "https://jithinks.netlify.app/", "contributions": [ - "code" + "code", + "example" ] }, { @@ -6445,6 +6459,202 @@ "code", "doc" ] + }, + { + "login": "philyawj", + "name": "Jordan Philyaw", + "avatar_url": "https://avatars.githubusercontent.com/u/37485853?v=4", + "profile": "http://jordanphilyaw.com", + "contributions": [ + "doc" + ] + }, + { + "login": "thrly", + "name": "oliver thurley", + "avatar_url": "https://avatars.githubusercontent.com/u/22343302?v=4", + "profile": "http://oliverthurley.co.uk", + "contributions": [ + "doc", + "code" + ] + }, + { + "login": "lirenjie95", + "name": "Renjie Li", + "avatar_url": "https://avatars.githubusercontent.com/u/17038472?v=4", + "profile": "https://github.com/lirenjie95", + "contributions": [ + "doc", + "code" + ] + }, + { + "login": "Vaivaswat2244", + "name": "Vaivaswat Dubey", + "avatar_url": "https://avatars.githubusercontent.com/u/113991324?v=4", + "profile": "https://github.com/Vaivaswat2244", + "contributions": [ + "code" + ] + }, + { + "login": "xinemata", + "name": "Xin Xin", + "avatar_url": "https://avatars.githubusercontent.com/u/9159424?v=4", + "profile": "https://github.com/xinemata", + "contributions": [ + "eventOrganizing", + "tutorial", + "talk", + "mentoring", + "ideas" + ] + }, + { + "login": "akkarn1689", + "name": "Ashish Karn", + "avatar_url": "https://avatars.githubusercontent.com/u/82334486?v=4", + "profile": "https://karn-ashish-16.onrender.com/", + "contributions": [ + "code" + ] + }, + { + "login": "dkessner", + "name": "Darren Kessner", + "avatar_url": "https://avatars.githubusercontent.com/u/1002034?v=4", + "profile": "https://dkessner.github.io/", + "contributions": [ + "example" + ] + }, + { + "login": "AnimeshSinha1309", + "name": "Animesh Sinha", + "avatar_url": "https://avatars.githubusercontent.com/u/9739218?v=4", + "profile": "http://www.facebook.com/AnimeshSinha1309", + "contributions": [ + "example" + ] + }, + { + "login": "katlich112358", + "name": "Kathryn Lichlyter", + "avatar_url": "https://avatars.githubusercontent.com/u/59926191?v=4", + "profile": "https://github.com/katlich112358", + "contributions": [ + "example" + ] + }, + { + "login": "geealbers", + "name": "Greg Albers", + "avatar_url": "https://avatars.githubusercontent.com/u/7796401?v=4", + "profile": "https://geealbers.net/", + "contributions": [ + "example" + ] + }, + { + "login": "macarena", + "name": "Marco Macarena", + "avatar_url": "https://avatars.githubusercontent.com/u/79822?v=4", + "profile": "http://macarena.pro.br", + "contributions": [ + "example" + ] + }, + { + "login": "khamiltonuk", + "name": "Kristian Hamilton", + "avatar_url": "https://avatars.githubusercontent.com/u/4013283?v=4", + "profile": "http://www.khamilton.co.uk", + "contributions": [ + "example" + ] + }, + { + "login": "keshavg2", + "name": "Keshav Gupta", + "avatar_url": "https://avatars.githubusercontent.com/u/34001173?v=4", + "profile": "https://keshav-portfolio-resume.netlify.app/", + "contributions": [ + "example" + ] + }, + { + "login": "riteshsp2000", + "name": "Ritesh Patil", + "avatar_url": "https://avatars.githubusercontent.com/u/56112399?v=4", + "profile": "http://riteshpatil.com", + "contributions": [ + "example" + ] + }, + { + "login": "gabrielsroka", + "name": "Gabriel Sroka", + "avatar_url": "https://avatars.githubusercontent.com/u/14354736?v=4", + "profile": "https://gabrielsroka.github.io", + "contributions": [ + "example" + ] + }, + { + "login": "kcconch", + "name": "Casey Conchinha", + "avatar_url": "https://avatars.githubusercontent.com/u/26172283?v=4", + "profile": "http://kccon.ch", + "contributions": [ + "example" + ] + }, + { + "login": "davidblitz", + "name": "davidblitz", + "avatar_url": "https://avatars.githubusercontent.com/u/13055497?v=4", + "profile": "https://github.com/davidblitz", + "contributions": [ + "example" + ] + }, + { + "login": "crh82", + "name": "crh82", + "avatar_url": "https://avatars.githubusercontent.com/u/103348212?v=4", + "profile": "https://github.com/crh82", + "contributions": [ + "example" + ] + }, + { + "login": "", + "name": "Aaron Welles", + "avatar_url": "https://hello-assets.p5js.org/placeholder_avatar.png", + "profile": "", + "contributions": [ + "example" + ] + }, + { + "login": "seyko1", + "name": "Seyko", + "avatar_url": "https://avatars.githubusercontent.com/u/26870879?v=4", + "profile": "https://github.com/seyko1", + "contributions": [ + "code" + ] + }, + { + "login": "thekinardist", + "name": "Xevi H. Aqeel", + "avatar_url": "https://avatars.githubusercontent.com/u/21345916?v=4", + "profile": "http://kinardist.media", + "contributions": [ + "example", + "design" + ] } ], "repoType": "github", diff --git a/README.md b/README.md index 19505fbabc..72088f2c64 100644 --- a/README.md +++ b/README.md @@ -75,8 +75,8 @@ Current Lead/Mentor * [@limzykenneth](https://github.com/limzykenneth) - p5.js Mentor,2023-present Lead/Mentor Alumni -* [@lmccart](https://github.com/lmccart)- p5.js Creator -* [@qianqianye](https://github.com/qianqianye) - p5.js Lead,2021-2024 +* [@lmccart](https://github.com/lmccart) - p5.js Creator +* [@qianqianye](https://github.com/qianqianye) - p5.js Lead,2021-present (on leave) * [@outofambit](https://github.com/outofambit) - p5.js Co-Lead 2021-22, Mentor 2022-2023 * [@mcturner1995](https://github.com/mcturner1995) - p5.js Lead 2020 @@ -353,7 +353,7 @@ We recognize all types of contributions. This project follows the [all-contribut
\n\n <%=name%>\n\n \n <% if (final) { %>\n constant\n <% } else if (itemtype) { %>\n <%=itemtype%> \n <% } %>\n\n <% if (className) { %>\n in <%=className%>\n <% } %>\n\n <% if (typeof is_constructor !== \'undefined\' && is_constructor) { %>\n constructor\n <% } %>\n \n\n
';}); + define('text!tpl/search_suggestion.html',[],function () { return '\n\n <%=name%>\n\n \n <% if (final) { %>\n constant\n <% } else if (itemtype) { %>\n <%=itemtype%> \n <% } %>\n\n <% if (className) { %>\n in <%=className%>\n <% } %>\n\n <% if (typeof is_constructor !== \'undefined\' && is_constructor) { %>\n constructor\n <% } %>\n \n\n
';}); -/*! + /*! * typeahead.js 0.10.2 * https://github.com/twitter/typeahead.js * Copyright 2013-2014 Twitter, Inc. and other contributors; Licensed MIT */ -define('typeahead',[], function() { + define('typeahead',[], function() { -//(function($) { + //(function($) { var _ = { - isMsie: function() { - return /(msie|trident)/i.test(navigator.userAgent) ? navigator.userAgent.match(/(msie |rv:)(\d+(.\d+)?)/i)[2] : false; - }, - isBlankString: function(str) { - return !str || /^\s*$/.test(str); - }, - escapeRegExChars: function(str) { - return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"); - }, - isString: function(obj) { - return typeof obj === "string"; - }, - isNumber: function(obj) { - return typeof obj === "number"; - }, - isArray: $.isArray, - isFunction: $.isFunction, - isObject: $.isPlainObject, - isUndefined: function(obj) { - return typeof obj === "undefined"; - }, - bind: $.proxy, - each: function(collection, cb) { - $.each(collection, reverseArgs); - function reverseArgs(index, value) { - return cb(value, index); - } - }, - map: $.map, - filter: $.grep, - every: function(obj, test) { - var result = true; - if (!obj) { - return result; - } - $.each(obj, function(key, val) { - if (!(result = test.call(null, val, key, obj))) { - return false; - } - }); - return !!result; - }, - some: function(obj, test) { - var result = false; - if (!obj) { - return result; - } - $.each(obj, function(key, val) { - if (result = test.call(null, val, key, obj)) { - return false; - } - }); - return !!result; - }, - mixin: $.extend, - getUniqueId: function() { - var counter = 0; - return function() { - return counter++; - }; - }(), - templatify: function templatify(obj) { - return $.isFunction(obj) ? obj : template; - function template() { - return String(obj); - } - }, - defer: function(fn) { - setTimeout(fn, 0); - }, - debounce: function(func, wait, immediate) { - var timeout, result; - return function() { - var context = this, args = arguments, later, callNow; - later = function() { - timeout = null; - if (!immediate) { - result = func.apply(context, args); - } - }; - callNow = immediate && !timeout; - clearTimeout(timeout); - timeout = setTimeout(later, wait); - if (callNow) { - result = func.apply(context, args); - } - return result; - }; - }, - throttle: function(func, wait) { - var context, args, timeout, result, previous, later; - previous = 0; - later = function() { - previous = new Date(); - timeout = null; - result = func.apply(context, args); - }; - return function() { - var now = new Date(), remaining = wait - (now - previous); - context = this; - args = arguments; - if (remaining <= 0) { - clearTimeout(timeout); - timeout = null; - previous = now; - result = func.apply(context, args); - } else if (!timeout) { - timeout = setTimeout(later, remaining); - } - return result; - }; - }, - noop: function() {} - }; - var VERSION = "0.10.2"; - var tokenizers = function(root) { - return { - nonword: nonword, - whitespace: whitespace, - obj: { - nonword: getObjTokenizer(nonword), - whitespace: getObjTokenizer(whitespace) - } - }; - function whitespace(s) { - return s.split(/\s+/); - } - function nonword(s) { - return s.split(/\W+/); + isMsie: function() { + return /(msie|trident)/i.test(navigator.userAgent) ? navigator.userAgent.match(/(msie |rv:)(\d+(.\d+)?)/i)[2] : false; + }, + isBlankString: function(str) { + return !str || /^\s*$/.test(str); + }, + escapeRegExChars: function(str) { + return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&'); + }, + isString: function(obj) { + return typeof obj === 'string'; + }, + isNumber: function(obj) { + return typeof obj === 'number'; + }, + isArray: $.isArray, + isFunction: $.isFunction, + isObject: $.isPlainObject, + isUndefined: function(obj) { + return typeof obj === 'undefined'; + }, + bind: $.proxy, + each: function(collection, cb) { + $.each(collection, reverseArgs); + function reverseArgs(index, value) { + return cb(value, index); } - function getObjTokenizer(tokenizer) { - return function setKey(key) { - return function tokenize(o) { - return tokenizer(o[key]); - }; - }; + }, + map: $.map, + filter: $.grep, + every: function(obj, test) { + var result = true; + if (!obj) { + return result; } - }(); - var LruCache = function() { - function LruCache(maxSize) { - this.maxSize = maxSize || 100; - this.size = 0; - this.hash = {}; - this.list = new List(); + $.each(obj, function(key, val) { + if (!(result = test.call(null, val, key, obj))) { + return false; + } + }); + return !!result; + }, + some: function(obj, test) { + var result = false; + if (!obj) { + return result; } - _.mixin(LruCache.prototype, { - set: function set(key, val) { - var tailItem = this.list.tail, node; - if (this.size >= this.maxSize) { - this.list.remove(tailItem); - delete this.hash[tailItem.key]; - } - if (node = this.hash[key]) { - node.val = val; - this.list.moveToFront(node); - } else { - node = new Node(key, val); - this.list.add(node); - this.hash[key] = node; - this.size++; - } - }, - get: function get(key) { - var node = this.hash[key]; - if (node) { - this.list.moveToFront(node); - return node.val; - } - } + $.each(obj, function(key, val) { + if (result = test.call(null, val, key, obj)) { + return false; + } }); - function List() { - this.head = this.tail = null; + return !!result; + }, + mixin: $.extend, + getUniqueId: function() { + var counter = 0; + return function() { + return counter++; + }; + }(), + templatify: function templatify(obj) { + return $.isFunction(obj) ? obj : template; + function template() { + return String(obj); } - _.mixin(List.prototype, { - add: function add(node) { - if (this.head) { - node.next = this.head; - this.head.prev = node; - } - this.head = node; - this.tail = this.tail || node; - }, - remove: function remove(node) { - node.prev ? node.prev.next = node.next : this.head = node.next; - node.next ? node.next.prev = node.prev : this.tail = node.prev; - }, - moveToFront: function(node) { - this.remove(node); - this.add(node); + }, + defer: function(fn) { + setTimeout(fn, 0); + }, + debounce: function(func, wait, immediate) { + var timeout, result; + return function() { + var context = this, args = arguments, later, callNow; + later = function() { + timeout = null; + if (!immediate) { + result = func.apply(context, args); } - }); - function Node(key, val) { - this.key = key; - this.val = val; - this.prev = this.next = null; + }; + callNow = immediate && !timeout; + clearTimeout(timeout); + timeout = setTimeout(later, wait); + if (callNow) { + result = func.apply(context, args); + } + return result; + }; + }, + throttle: function(func, wait) { + var context, args, timeout, result, previous, later; + previous = 0; + later = function() { + previous = new Date(); + timeout = null; + result = func.apply(context, args); + }; + return function() { + var now = new Date(), remaining = wait - (now - previous); + context = this; + args = arguments; + if (remaining <= 0) { + clearTimeout(timeout); + timeout = null; + previous = now; + result = func.apply(context, args); + } else if (!timeout) { + timeout = setTimeout(later, remaining); + } + return result; + }; + }, + noop: function() {} + }; + var VERSION = '0.10.2'; + var tokenizers = function(root) { + return { + nonword: nonword, + whitespace: whitespace, + obj: { + nonword: getObjTokenizer(nonword), + whitespace: getObjTokenizer(whitespace) } - return LruCache; + }; + function whitespace(s) { + return s.split(/\s+/); + } + function nonword(s) { + return s.split(/\W+/); + } + function getObjTokenizer(tokenizer) { + return function setKey(key) { + return function tokenize(o) { + return tokenizer(o[key]); + }; + }; + } }(); - var PersistentStorage = function() { - var ls, methods; - try { - ls = window.localStorage; - ls.setItem("~~~", "!"); - ls.removeItem("~~~"); - } catch (err) { - ls = null; - } - function PersistentStorage(namespace) { - this.prefix = [ "__", namespace, "__" ].join(""); - this.ttlKey = "__ttl__"; - this.keyMatcher = new RegExp("^" + this.prefix); - } - if (ls && window.JSON) { - methods = { - _prefix: function(key) { - return this.prefix + key; - }, - _ttlKey: function(key) { - return this._prefix(key) + this.ttlKey; - }, - get: function(key) { - if (this.isExpired(key)) { - this.remove(key); - } - return decode(ls.getItem(this._prefix(key))); - }, - set: function(key, val, ttl) { - if (_.isNumber(ttl)) { - ls.setItem(this._ttlKey(key), encode(now() + ttl)); - } else { - ls.removeItem(this._ttlKey(key)); - } - return ls.setItem(this._prefix(key), encode(val)); - }, - remove: function(key) { - ls.removeItem(this._ttlKey(key)); - ls.removeItem(this._prefix(key)); - return this; - }, - clear: function() { - var i, key, keys = [], len = ls.length; - for (i = 0; i < len; i++) { - if ((key = ls.key(i)).match(this.keyMatcher)) { - keys.push(key.replace(this.keyMatcher, "")); - } - } - for (i = keys.length; i--; ) { - this.remove(keys[i]); - } - return this; - }, - isExpired: function(key) { - var ttl = decode(ls.getItem(this._ttlKey(key))); - return _.isNumber(ttl) && now() > ttl ? true : false; - } - }; - } else { - methods = { - get: _.noop, - set: _.noop, - remove: _.noop, - clear: _.noop, - isExpired: _.noop - }; - } - _.mixin(PersistentStorage.prototype, methods); - return PersistentStorage; - function now() { - return new Date().getTime(); - } - function encode(val) { - return JSON.stringify(_.isUndefined(val) ? null : val); + var LruCache = function() { + function LruCache(maxSize) { + this.maxSize = maxSize || 100; + this.size = 0; + this.hash = {}; + this.list = new List(); + } + _.mixin(LruCache.prototype, { + set: function set(key, val) { + var tailItem = this.list.tail, node; + if (this.size >= this.maxSize) { + this.list.remove(tailItem); + delete this.hash[tailItem.key]; + } + if (node = this.hash[key]) { + node.val = val; + this.list.moveToFront(node); + } else { + node = new Node(key, val); + this.list.add(node); + this.hash[key] = node; + this.size++; + } + }, + get: function get(key) { + var node = this.hash[key]; + if (node) { + this.list.moveToFront(node); + return node.val; + } } - function decode(val) { - return JSON.parse(val); + }); + function List() { + this.head = this.tail = null; + } + _.mixin(List.prototype, { + add: function add(node) { + if (this.head) { + node.next = this.head; + this.head.prev = node; + } + this.head = node; + this.tail = this.tail || node; + }, + remove: function remove(node) { + node.prev ? node.prev.next = node.next : this.head = node.next; + node.next ? node.next.prev = node.prev : this.tail = node.prev; + }, + moveToFront: function(node) { + this.remove(node); + this.add(node); } + }); + function Node(key, val) { + this.key = key; + this.val = val; + this.prev = this.next = null; + } + return LruCache; }(); - var Transport = function() { - var pendingRequestsCount = 0, pendingRequests = {}, maxPendingRequests = 6, requestCache = new LruCache(10); - function Transport(o) { - o = o || {}; - this._send = o.transport ? callbackToDeferred(o.transport) : $.ajax; - this._get = o.rateLimiter ? o.rateLimiter(this._get) : this._get; - } - Transport.setMaxPendingRequests = function setMaxPendingRequests(num) { - maxPendingRequests = num; - }; - Transport.resetCache = function clearCache() { - requestCache = new LruCache(10); - }; - _.mixin(Transport.prototype, { - _get: function(url, o, cb) { - var that = this, jqXhr; - if (jqXhr = pendingRequests[url]) { - jqXhr.done(done).fail(fail); - } else if (pendingRequestsCount < maxPendingRequests) { - pendingRequestsCount++; - pendingRequests[url] = this._send(url, o).done(done).fail(fail).always(always); - } else { - this.onDeckRequestArgs = [].slice.call(arguments, 0); - } - function done(resp) { - cb && cb(null, resp); - requestCache.set(url, resp); - } - function fail() { - cb && cb(true); - } - function always() { - pendingRequestsCount--; - delete pendingRequests[url]; - if (that.onDeckRequestArgs) { - that._get.apply(that, that.onDeckRequestArgs); - that.onDeckRequestArgs = null; - } - } - }, - get: function(url, o, cb) { - var resp; - if (_.isFunction(o)) { - cb = o; - o = {}; - } - if (resp = requestCache.get(url)) { - _.defer(function() { - cb && cb(null, resp); - }); - } else { - this._get(url, o, cb); - } - return !!resp; + var PersistentStorage = function() { + var ls, methods; + try { + ls = window.localStorage; + ls.setItem('~~~', '!'); + ls.removeItem('~~~'); + } catch (err) { + ls = null; + } + function PersistentStorage(namespace) { + this.prefix = [ '__', namespace, '__' ].join(''); + this.ttlKey = '__ttl__'; + this.keyMatcher = new RegExp('^' + this.prefix); + } + if (ls && window.JSON) { + methods = { + _prefix: function(key) { + return this.prefix + key; + }, + _ttlKey: function(key) { + return this._prefix(key) + this.ttlKey; + }, + get: function(key) { + if (this.isExpired(key)) { + this.remove(key); } - }); - return Transport; - function callbackToDeferred(fn) { - return function customSendWrapper(url, o) { - var deferred = $.Deferred(); - fn(url, o, onSuccess, onError); - return deferred; - function onSuccess(resp) { - _.defer(function() { - deferred.resolve(resp); - }); - } - function onError(err) { - _.defer(function() { - deferred.reject(err); - }); - } - }; - } - }(); - var SearchIndex = function() { - function SearchIndex(o) { - o = o || {}; - if (!o.datumTokenizer || !o.queryTokenizer) { - $.error("datumTokenizer and queryTokenizer are both required"); + return decode(ls.getItem(this._prefix(key))); + }, + set: function(key, val, ttl) { + if (_.isNumber(ttl)) { + ls.setItem(this._ttlKey(key), encode(now() + ttl)); + } else { + ls.removeItem(this._ttlKey(key)); } - this.datumTokenizer = o.datumTokenizer; - this.queryTokenizer = o.queryTokenizer; - this.reset(); - } - _.mixin(SearchIndex.prototype, { - bootstrap: function bootstrap(o) { - this.datums = o.datums; - this.trie = o.trie; - }, - add: function(data) { - var that = this; - data = _.isArray(data) ? data : [ data ]; - _.each(data, function(datum) { - var id, tokens; - id = that.datums.push(datum) - 1; - tokens = normalizeTokens(that.datumTokenizer(datum)); - _.each(tokens, function(token) { - var node, chars, ch; - node = that.trie; - chars = token.split(""); - while (ch = chars.shift()) { - node = node.children[ch] || (node.children[ch] = newNode()); - node.ids.push(id); - } - }); - }); - }, - get: function get(query) { - var that = this, tokens, matches; - tokens = normalizeTokens(this.queryTokenizer(query)); - _.each(tokens, function(token) { - var node, chars, ch, ids; - if (matches && matches.length === 0) { - return false; - } - node = that.trie; - chars = token.split(""); - while (node && (ch = chars.shift())) { - node = node.children[ch]; - } - if (node && chars.length === 0) { - ids = node.ids.slice(0); - matches = matches ? getIntersection(matches, ids) : ids; - } else { - matches = []; - return false; - } - }); - return matches ? _.map(unique(matches), function(id) { - return that.datums[id]; - }) : []; - }, - reset: function reset() { - this.datums = []; - this.trie = newNode(); - }, - serialize: function serialize() { - return { - datums: this.datums, - trie: this.trie - }; + return ls.setItem(this._prefix(key), encode(val)); + }, + remove: function(key) { + ls.removeItem(this._ttlKey(key)); + ls.removeItem(this._prefix(key)); + return this; + }, + clear: function() { + var i, key, keys = [], len = ls.length; + for (i = 0; i < len; i++) { + if ((key = ls.key(i)).match(this.keyMatcher)) { + keys.push(key.replace(this.keyMatcher, '')); + } } - }); - return SearchIndex; - function normalizeTokens(tokens) { - tokens = _.filter(tokens, function(token) { - return !!token; - }); - tokens = _.map(tokens, function(token) { - return token.toLowerCase(); + for (i = keys.length; i--; ) { + this.remove(keys[i]); + } + return this; + }, + isExpired: function(key) { + var ttl = decode(ls.getItem(this._ttlKey(key))); + return _.isNumber(ttl) && now() > ttl ? true : false; + } + }; + } else { + methods = { + get: _.noop, + set: _.noop, + remove: _.noop, + clear: _.noop, + isExpired: _.noop + }; + } + _.mixin(PersistentStorage.prototype, methods); + return PersistentStorage; + function now() { + return new Date().getTime(); + } + function encode(val) { + return JSON.stringify(_.isUndefined(val) ? null : val); + } + function decode(val) { + return JSON.parse(val); + } + }(); + var Transport = function() { + var pendingRequestsCount = 0, pendingRequests = {}, maxPendingRequests = 6, requestCache = new LruCache(10); + function Transport(o) { + o = o || {}; + this._send = o.transport ? callbackToDeferred(o.transport) : $.ajax; + this._get = o.rateLimiter ? o.rateLimiter(this._get) : this._get; + } + Transport.setMaxPendingRequests = function setMaxPendingRequests(num) { + maxPendingRequests = num; + }; + Transport.resetCache = function clearCache() { + requestCache = new LruCache(10); + }; + _.mixin(Transport.prototype, { + _get: function(url, o, cb) { + var that = this, jqXhr; + if (jqXhr = pendingRequests[url]) { + jqXhr.done(done).fail(fail); + } else if (pendingRequestsCount < maxPendingRequests) { + pendingRequestsCount++; + pendingRequests[url] = this._send(url, o).done(done).fail(fail).always(always); + } else { + this.onDeckRequestArgs = [].slice.call(arguments, 0); + } + function done(resp) { + cb && cb(null, resp); + requestCache.set(url, resp); + } + function fail() { + cb && cb(true); + } + function always() { + pendingRequestsCount--; + delete pendingRequests[url]; + if (that.onDeckRequestArgs) { + that._get.apply(that, that.onDeckRequestArgs); + that.onDeckRequestArgs = null; + } + } + }, + get: function(url, o, cb) { + var resp; + if (_.isFunction(o)) { + cb = o; + o = {}; + } + if (resp = requestCache.get(url)) { + _.defer(function() { + cb && cb(null, resp); }); - return tokens; + } else { + this._get(url, o, cb); + } + return !!resp; } - function newNode() { - return { - ids: [], - children: {} - }; + }); + return Transport; + function callbackToDeferred(fn) { + return function customSendWrapper(url, o) { + var deferred = $.Deferred(); + fn(url, o, onSuccess, onError); + return deferred; + function onSuccess(resp) { + _.defer(function() { + deferred.resolve(resp); + }); + } + function onError(err) { + _.defer(function() { + deferred.reject(err); + }); + } + }; + } + }(); + var SearchIndex = function() { + function SearchIndex(o) { + o = o || {}; + if (!o.datumTokenizer || !o.queryTokenizer) { + $.error('datumTokenizer and queryTokenizer are both required'); } - function unique(array) { - var seen = {}, uniques = []; - for (var i = 0; i < array.length; i++) { - if (!seen[array[i]]) { - seen[array[i]] = true; - uniques.push(array[i]); - } + this.datumTokenizer = o.datumTokenizer; + this.queryTokenizer = o.queryTokenizer; + this.reset(); + } + _.mixin(SearchIndex.prototype, { + bootstrap: function bootstrap(o) { + this.datums = o.datums; + this.trie = o.trie; + }, + add: function(data) { + var that = this; + data = _.isArray(data) ? data : [ data ]; + _.each(data, function(datum) { + var id, tokens; + id = that.datums.push(datum) - 1; + tokens = normalizeTokens(that.datumTokenizer(datum)); + _.each(tokens, function(token) { + var node, chars, ch; + node = that.trie; + chars = token.split(''); + while (ch = chars.shift()) { + node = node.children[ch] || (node.children[ch] = newNode()); + node.ids.push(id); + } + }); + }); + }, + get: function get(query) { + var that = this, tokens, matches; + tokens = normalizeTokens(this.queryTokenizer(query)); + _.each(tokens, function(token) { + var node, chars, ch, ids; + if (matches && matches.length === 0) { + return false; } - return uniques; - } - function getIntersection(arrayA, arrayB) { - var ai = 0, bi = 0, intersection = []; - arrayA = arrayA.sort(compare); - arrayB = arrayB.sort(compare); - while (ai < arrayA.length && bi < arrayB.length) { - if (arrayA[ai] < arrayB[bi]) { - ai++; - } else if (arrayA[ai] > arrayB[bi]) { - bi++; - } else { - intersection.push(arrayA[ai]); - ai++; - bi++; - } + node = that.trie; + chars = token.split(''); + while (node && (ch = chars.shift())) { + node = node.children[ch]; } - return intersection; - function compare(a, b) { - return a - b; + if (node && chars.length === 0) { + ids = node.ids.slice(0); + matches = matches ? getIntersection(matches, ids) : ids; + } else { + matches = []; + return false; } + }); + return matches ? _.map(unique(matches), function(id) { + return that.datums[id]; + }) : []; + }, + reset: function reset() { + this.datums = []; + this.trie = newNode(); + }, + serialize: function serialize() { + return { + datums: this.datums, + trie: this.trie + }; } - }(); - var oParser = function() { + }); + return SearchIndex; + function normalizeTokens(tokens) { + tokens = _.filter(tokens, function(token) { + return !!token; + }); + tokens = _.map(tokens, function(token) { + return token.toLowerCase(); + }); + return tokens; + } + function newNode() { return { - local: getLocal, - prefetch: getPrefetch, - remote: getRemote + ids: [], + children: {} }; - function getLocal(o) { - return o.local || null; + } + function unique(array) { + var seen = {}, uniques = []; + for (var i = 0; i < array.length; i++) { + if (!seen[array[i]]) { + seen[array[i]] = true; + uniques.push(array[i]); + } } - function getPrefetch(o) { - var prefetch, defaults; - defaults = { - url: null, - thumbprint: "", - ttl: 24 * 60 * 60 * 1e3, - filter: null, - ajax: {} - }; - if (prefetch = o.prefetch || null) { - prefetch = _.isString(prefetch) ? { - url: prefetch - } : prefetch; - prefetch = _.mixin(defaults, prefetch); - prefetch.thumbprint = VERSION + prefetch.thumbprint; - prefetch.ajax.type = prefetch.ajax.type || "GET"; - prefetch.ajax.dataType = prefetch.ajax.dataType || "json"; - !prefetch.url && $.error("prefetch requires url to be set"); - } - return prefetch; + return uniques; + } + function getIntersection(arrayA, arrayB) { + var ai = 0, bi = 0, intersection = []; + arrayA = arrayA.sort(compare); + arrayB = arrayB.sort(compare); + while (ai < arrayA.length && bi < arrayB.length) { + if (arrayA[ai] < arrayB[bi]) { + ai++; + } else if (arrayA[ai] > arrayB[bi]) { + bi++; + } else { + intersection.push(arrayA[ai]); + ai++; + bi++; + } } - function getRemote(o) { - var remote, defaults; - defaults = { - url: null, - wildcard: "%QUERY", - replace: null, - rateLimitBy: "debounce", - rateLimitWait: 300, - send: null, - filter: null, - ajax: {} - }; - if (remote = o.remote || null) { - remote = _.isString(remote) ? { - url: remote - } : remote; - remote = _.mixin(defaults, remote); - remote.rateLimiter = /^throttle$/i.test(remote.rateLimitBy) ? byThrottle(remote.rateLimitWait) : byDebounce(remote.rateLimitWait); - remote.ajax.type = remote.ajax.type || "GET"; - remote.ajax.dataType = remote.ajax.dataType || "json"; - delete remote.rateLimitBy; - delete remote.rateLimitWait; - !remote.url && $.error("remote requires url to be set"); - } - return remote; - function byDebounce(wait) { - return function(fn) { - return _.debounce(fn, wait); - }; - } - function byThrottle(wait) { - return function(fn) { - return _.throttle(fn, wait); - }; - } + return intersection; + function compare(a, b) { + return a - b; } + } }(); - (function(root) { - var old, keys; - old = root.Bloodhound; - keys = { - data: "data", - protocol: "protocol", - thumbprint: "thumbprint" + var oParser = function() { + return { + local: getLocal, + prefetch: getPrefetch, + remote: getRemote + }; + function getLocal(o) { + return o.local || null; + } + function getPrefetch(o) { + var prefetch, defaults; + defaults = { + url: null, + thumbprint: '', + ttl: 24 * 60 * 60 * 1e3, + filter: null, + ajax: {} }; - root.Bloodhound = Bloodhound; - function Bloodhound(o) { - if (!o || !o.local && !o.prefetch && !o.remote) { - $.error("one of local, prefetch, or remote is required"); - } - this.limit = o.limit || 5; - this.sorter = getSorter(o.sorter); - this.dupDetector = o.dupDetector || ignoreDuplicates; - this.local = oParser.local(o); - this.prefetch = oParser.prefetch(o); - this.remote = oParser.remote(o); - this.cacheKey = this.prefetch ? this.prefetch.cacheKey || this.prefetch.url : null; - this.index = new SearchIndex({ - datumTokenizer: o.datumTokenizer, - queryTokenizer: o.queryTokenizer - }); - this.storage = this.cacheKey ? new PersistentStorage(this.cacheKey) : null; + if (prefetch = o.prefetch || null) { + prefetch = _.isString(prefetch) ? { + url: prefetch + } : prefetch; + prefetch = _.mixin(defaults, prefetch); + prefetch.thumbprint = VERSION + prefetch.thumbprint; + prefetch.ajax.type = prefetch.ajax.type || 'GET'; + prefetch.ajax.dataType = prefetch.ajax.dataType || 'json'; + !prefetch.url && $.error('prefetch requires url to be set'); } - Bloodhound.noConflict = function noConflict() { - root.Bloodhound = old; - return Bloodhound; + return prefetch; + } + function getRemote(o) { + var remote, defaults; + defaults = { + url: null, + wildcard: '%QUERY', + replace: null, + rateLimitBy: 'debounce', + rateLimitWait: 300, + send: null, + filter: null, + ajax: {} }; - Bloodhound.tokenizers = tokenizers; - _.mixin(Bloodhound.prototype, { - _loadPrefetch: function loadPrefetch(o) { - var that = this, serialized, deferred; - if (serialized = this._readFromStorage(o.thumbprint)) { - this.index.bootstrap(serialized); - deferred = $.Deferred().resolve(); - } else { - deferred = $.ajax(o.url, o.ajax).done(handlePrefetchResponse); - } - return deferred; - function handlePrefetchResponse(resp) { - that.clear(); - that.add(o.filter ? o.filter(resp) : resp); - that._saveToStorage(that.index.serialize(), o.thumbprint, o.ttl); - } - }, - _getFromRemote: function getFromRemote(query, cb) { - var that = this, url, uriEncodedQuery; - query = query || ""; - uriEncodedQuery = encodeURIComponent(query); - url = this.remote.replace ? this.remote.replace(this.remote.url, query) : this.remote.url.replace(this.remote.wildcard, uriEncodedQuery); - return this.transport.get(url, this.remote.ajax, handleRemoteResponse); - function handleRemoteResponse(err, resp) { - err ? cb([]) : cb(that.remote.filter ? that.remote.filter(resp) : resp); - } - }, - _saveToStorage: function saveToStorage(data, thumbprint, ttl) { - if (this.storage) { - this.storage.set(keys.data, data, ttl); - this.storage.set(keys.protocol, location.protocol, ttl); - this.storage.set(keys.thumbprint, thumbprint, ttl); - } - }, - _readFromStorage: function readFromStorage(thumbprint) { - var stored = {}, isExpired; - if (this.storage) { - stored.data = this.storage.get(keys.data); - stored.protocol = this.storage.get(keys.protocol); - stored.thumbprint = this.storage.get(keys.thumbprint); - } - isExpired = stored.thumbprint !== thumbprint || stored.protocol !== location.protocol; - return stored.data && !isExpired ? stored.data : null; - }, - _initialize: function initialize() { - var that = this, local = this.local, deferred; - deferred = this.prefetch ? this._loadPrefetch(this.prefetch) : $.Deferred().resolve(); - local && deferred.done(addLocalToIndex); - this.transport = this.remote ? new Transport(this.remote) : null; - return this.initPromise = deferred.promise(); - function addLocalToIndex() { - that.add(_.isFunction(local) ? local() : local); - } - }, - initialize: function initialize(force) { - return !this.initPromise || force ? this._initialize() : this.initPromise; - }, - add: function add(data) { - this.index.add(data); - }, - get: function get(query, cb) { - var that = this, matches = [], cacheHit = false; - matches = this.index.get(query); - matches = this.sorter(matches).slice(0, this.limit); - if (matches.length < this.limit && this.transport) { - cacheHit = this._getFromRemote(query, returnRemoteMatches); - } - if (!cacheHit) { - (matches.length > 0 || !this.transport) && cb && cb(matches); - } - function returnRemoteMatches(remoteMatches) { - var matchesWithBackfill = matches.slice(0); - _.each(remoteMatches, function(remoteMatch) { - var isDuplicate; - isDuplicate = _.some(matchesWithBackfill, function(match) { - return that.dupDetector(remoteMatch, match); - }); - !isDuplicate && matchesWithBackfill.push(remoteMatch); - return matchesWithBackfill.length < that.limit; - }); - cb && cb(that.sorter(matchesWithBackfill)); - } - }, - clear: function clear() { - this.index.reset(); - }, - clearPrefetchCache: function clearPrefetchCache() { - this.storage && this.storage.clear(); - }, - clearRemoteCache: function clearRemoteCache() { - this.transport && Transport.resetCache(); - }, - ttAdapter: function ttAdapter() { - return _.bind(this.get, this); - } - }); - return Bloodhound; - function getSorter(sortFn) { - return _.isFunction(sortFn) ? sort : noSort; - function sort(array) { - return array.sort(sortFn); - } - function noSort(array) { - return array; - } + if (remote = o.remote || null) { + remote = _.isString(remote) ? { + url: remote + } : remote; + remote = _.mixin(defaults, remote); + remote.rateLimiter = /^throttle$/i.test(remote.rateLimitBy) ? byThrottle(remote.rateLimitWait) : byDebounce(remote.rateLimitWait); + remote.ajax.type = remote.ajax.type || 'GET'; + remote.ajax.dataType = remote.ajax.dataType || 'json'; + delete remote.rateLimitBy; + delete remote.rateLimitWait; + !remote.url && $.error('remote requires url to be set'); } - function ignoreDuplicates() { - return false; + return remote; + function byDebounce(wait) { + return function(fn) { + return _.debounce(fn, wait); + }; } - })(this); - var html = { - wrapper: '', - dropdown: '', - dataset: '', - suggestions: '', - suggestion: '' - }; - var css = { - wrapper: { - position: "relative", - display: "inline-block" + function byThrottle(wait) { + return function(fn) { + return _.throttle(fn, wait); + }; + } + } + }(); + (function(root) { + var old, keys; + old = root.Bloodhound; + keys = { + data: 'data', + protocol: 'protocol', + thumbprint: 'thumbprint' + }; + root.Bloodhound = Bloodhound; + function Bloodhound(o) { + if (!o || !o.local && !o.prefetch && !o.remote) { + $.error('one of local, prefetch, or remote is required'); + } + this.limit = o.limit || 5; + this.sorter = getSorter(o.sorter); + this.dupDetector = o.dupDetector || ignoreDuplicates; + this.local = oParser.local(o); + this.prefetch = oParser.prefetch(o); + this.remote = oParser.remote(o); + this.cacheKey = this.prefetch ? this.prefetch.cacheKey || this.prefetch.url : null; + this.index = new SearchIndex({ + datumTokenizer: o.datumTokenizer, + queryTokenizer: o.queryTokenizer + }); + this.storage = this.cacheKey ? new PersistentStorage(this.cacheKey) : null; + } + Bloodhound.noConflict = function noConflict() { + root.Bloodhound = old; + return Bloodhound; + }; + Bloodhound.tokenizers = tokenizers; + _.mixin(Bloodhound.prototype, { + _loadPrefetch: function loadPrefetch(o) { + var that = this, serialized, deferred; + if (serialized = this._readFromStorage(o.thumbprint)) { + this.index.bootstrap(serialized); + deferred = $.Deferred().resolve(); + } else { + deferred = $.ajax(o.url, o.ajax).done(handlePrefetchResponse); + } + return deferred; + function handlePrefetchResponse(resp) { + that.clear(); + that.add(o.filter ? o.filter(resp) : resp); + that._saveToStorage(that.index.serialize(), o.thumbprint, o.ttl); + } }, - hint: { - position: "absolute", - top: "0", - left: "0", - borderColor: "transparent", - boxShadow: "none" + _getFromRemote: function getFromRemote(query, cb) { + var that = this, url, uriEncodedQuery; + query = query || ''; + uriEncodedQuery = encodeURIComponent(query); + url = this.remote.replace ? this.remote.replace(this.remote.url, query) : this.remote.url.replace(this.remote.wildcard, uriEncodedQuery); + return this.transport.get(url, this.remote.ajax, handleRemoteResponse); + function handleRemoteResponse(err, resp) { + err ? cb([]) : cb(that.remote.filter ? that.remote.filter(resp) : resp); + } }, - input: { - position: "relative", - verticalAlign: "top", - backgroundColor: "transparent" + _saveToStorage: function saveToStorage(data, thumbprint, ttl) { + if (this.storage) { + this.storage.set(keys.data, data, ttl); + this.storage.set(keys.protocol, location.protocol, ttl); + this.storage.set(keys.thumbprint, thumbprint, ttl); + } }, - inputWithNoHint: { - position: "relative", - verticalAlign: "top" + _readFromStorage: function readFromStorage(thumbprint) { + var stored = {}, isExpired; + if (this.storage) { + stored.data = this.storage.get(keys.data); + stored.protocol = this.storage.get(keys.protocol); + stored.thumbprint = this.storage.get(keys.thumbprint); + } + isExpired = stored.thumbprint !== thumbprint || stored.protocol !== location.protocol; + return stored.data && !isExpired ? stored.data : null; }, - dropdown: { - position: "absolute", - top: "100%", - left: "0", - zIndex: "100", - display: "none" + _initialize: function initialize() { + var that = this, local = this.local, deferred; + deferred = this.prefetch ? this._loadPrefetch(this.prefetch) : $.Deferred().resolve(); + local && deferred.done(addLocalToIndex); + this.transport = this.remote ? new Transport(this.remote) : null; + return this.initPromise = deferred.promise(); + function addLocalToIndex() { + that.add(_.isFunction(local) ? local() : local); + } }, - suggestions: { - display: "block" + initialize: function initialize(force) { + return !this.initPromise || force ? this._initialize() : this.initPromise; + }, + add: function add(data) { + this.index.add(data); + }, + get: function get(query, cb) { + var that = this, matches = [], cacheHit = false; + matches = this.index.get(query); + matches = this.sorter(matches).slice(0, this.limit); + if (matches.length < this.limit && this.transport) { + cacheHit = this._getFromRemote(query, returnRemoteMatches); + } + if (!cacheHit) { + (matches.length > 0 || !this.transport) && cb && cb(matches); + } + function returnRemoteMatches(remoteMatches) { + var matchesWithBackfill = matches.slice(0); + _.each(remoteMatches, function(remoteMatch) { + var isDuplicate; + isDuplicate = _.some(matchesWithBackfill, function(match) { + return that.dupDetector(remoteMatch, match); + }); + !isDuplicate && matchesWithBackfill.push(remoteMatch); + return matchesWithBackfill.length < that.limit; + }); + cb && cb(that.sorter(matchesWithBackfill)); + } }, - suggestion: { - whiteSpace: "nowrap", - cursor: "pointer" + clear: function clear() { + this.index.reset(); }, - suggestionChild: { - whiteSpace: "normal" + clearPrefetchCache: function clearPrefetchCache() { + this.storage && this.storage.clear(); }, - ltr: { - left: "0", - right: "auto" + clearRemoteCache: function clearRemoteCache() { + this.transport && Transport.resetCache(); }, - rtl: { - left: "auto", - right: " 0" + ttAdapter: function ttAdapter() { + return _.bind(this.get, this); } + }); + return Bloodhound; + function getSorter(sortFn) { + return _.isFunction(sortFn) ? sort : noSort; + function sort(array) { + return array.sort(sortFn); + } + function noSort(array) { + return array; + } + } + function ignoreDuplicates() { + return false; + } + })(this); + var html = { + wrapper: '', + dropdown: '', + dataset: '', + suggestions: '', + suggestion: '' + }; + var css = { + wrapper: { + position: 'relative', + display: 'inline-block' + }, + hint: { + position: 'absolute', + top: '0', + left: '0', + borderColor: 'transparent', + boxShadow: 'none' + }, + input: { + position: 'relative', + verticalAlign: 'top', + backgroundColor: 'transparent' + }, + inputWithNoHint: { + position: 'relative', + verticalAlign: 'top' + }, + dropdown: { + position: 'absolute', + top: '100%', + left: '0', + zIndex: '100', + display: 'none' + }, + suggestions: { + display: 'block' + }, + suggestion: { + whiteSpace: 'nowrap', + cursor: 'pointer' + }, + suggestionChild: { + whiteSpace: 'normal' + }, + ltr: { + left: '0', + right: 'auto' + }, + rtl: { + left: 'auto', + right: ' 0' + } }; if (_.isMsie()) { - _.mixin(css.input, { - backgroundImage: "url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)" - }); + _.mixin(css.input, { + backgroundImage: 'url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)' + }); } if (_.isMsie() && _.isMsie() <= 7) { - _.mixin(css.input, { - marginTop: "-1px" - }); + _.mixin(css.input, { + marginTop: '-1px' + }); } var EventBus = function() { - var namespace = "typeahead:"; - function EventBus(o) { - if (!o || !o.el) { - $.error("EventBus initialized without el"); - } - this.$el = $(o.el); + var namespace = 'typeahead:'; + function EventBus(o) { + if (!o || !o.el) { + $.error('EventBus initialized without el'); } - _.mixin(EventBus.prototype, { - trigger: function(type) { - var args = [].slice.call(arguments, 1); - this.$el.trigger(namespace + type, args); - } - }); - return EventBus; + this.$el = $(o.el); + } + _.mixin(EventBus.prototype, { + trigger: function(type) { + var args = [].slice.call(arguments, 1); + this.$el.trigger(namespace + type, args); + } + }); + return EventBus; }(); var EventEmitter = function() { - var splitter = /\s+/, nextTick = getNextTick(); - return { - onSync: onSync, - onAsync: onAsync, - off: off, - trigger: trigger - }; - function on(method, types, cb, context) { - var type; - if (!cb) { - return this; - } - types = types.split(splitter); - cb = context ? bindContext(cb, context) : cb; - this._callbacks = this._callbacks || {}; - while (type = types.shift()) { - this._callbacks[type] = this._callbacks[type] || { - sync: [], - async: [] - }; - this._callbacks[type][method].push(cb); - } - return this; + var splitter = /\s+/, nextTick = getNextTick(); + return { + onSync: onSync, + onAsync: onAsync, + off: off, + trigger: trigger + }; + function on(method, types, cb, context) { + var type; + if (!cb) { + return this; } - function onAsync(types, cb, context) { - return on.call(this, "async", types, cb, context); + types = types.split(splitter); + cb = context ? bindContext(cb, context) : cb; + this._callbacks = this._callbacks || {}; + while (type = types.shift()) { + this._callbacks[type] = this._callbacks[type] || { + sync: [], + async: [] + }; + this._callbacks[type][method].push(cb); } - function onSync(types, cb, context) { - return on.call(this, "sync", types, cb, context); + return this; + } + function onAsync(types, cb, context) { + return on.call(this, 'async', types, cb, context); + } + function onSync(types, cb, context) { + return on.call(this, 'sync', types, cb, context); + } + function off(types) { + var type; + if (!this._callbacks) { + return this; } - function off(types) { - var type; - if (!this._callbacks) { - return this; - } - types = types.split(splitter); - while (type = types.shift()) { - delete this._callbacks[type]; - } - return this; + types = types.split(splitter); + while (type = types.shift()) { + delete this._callbacks[type]; } - function trigger(types) { - var type, callbacks, args, syncFlush, asyncFlush; - if (!this._callbacks) { - return this; - } - types = types.split(splitter); - args = [].slice.call(arguments, 1); - while ((type = types.shift()) && (callbacks = this._callbacks[type])) { - syncFlush = getFlush(callbacks.sync, this, [ type ].concat(args)); - asyncFlush = getFlush(callbacks.async, this, [ type ].concat(args)); - syncFlush() && nextTick(asyncFlush); - } - return this; + return this; + } + function trigger(types) { + var type, callbacks, args, syncFlush, asyncFlush; + if (!this._callbacks) { + return this; } - function getFlush(callbacks, context, args) { - return flush; - function flush() { - var cancelled; - for (var i = 0; !cancelled && i < callbacks.length; i += 1) { - cancelled = callbacks[i].apply(context, args) === false; - } - return !cancelled; - } + types = types.split(splitter); + args = [].slice.call(arguments, 1); + while ((type = types.shift()) && (callbacks = this._callbacks[type])) { + syncFlush = getFlush(callbacks.sync, this, [ type ].concat(args)); + asyncFlush = getFlush(callbacks.async, this, [ type ].concat(args)); + syncFlush() && nextTick(asyncFlush); } - function getNextTick() { - var nextTickFn; - if (window.setImmediate) { - nextTickFn = function nextTickSetImmediate(fn) { - setImmediate(function() { - fn(); - }); - }; - } else { - nextTickFn = function nextTickSetTimeout(fn) { - setTimeout(function() { - fn(); - }, 0); - }; - } - return nextTickFn; + return this; + } + function getFlush(callbacks, context, args) { + return flush; + function flush() { + var cancelled; + for (var i = 0; !cancelled && i < callbacks.length; i += 1) { + cancelled = callbacks[i].apply(context, args) === false; + } + return !cancelled; } - function bindContext(fn, context) { - return fn.bind ? fn.bind(context) : function() { - fn.apply(context, [].slice.call(arguments, 0)); - }; + } + function getNextTick() { + var nextTickFn; + if (window.setImmediate) { + nextTickFn = function nextTickSetImmediate(fn) { + setImmediate(function() { + fn(); + }); + }; + } else { + nextTickFn = function nextTickSetTimeout(fn) { + setTimeout(function() { + fn(); + }, 0); + }; } + return nextTickFn; + } + function bindContext(fn, context) { + return fn.bind ? fn.bind(context) : function() { + fn.apply(context, [].slice.call(arguments, 0)); + }; + } }(); var highlight = function(doc) { - var defaults = { - node: null, - pattern: null, - tagName: "strong", - className: null, - wordsOnly: false, - caseSensitive: false - }; - return function hightlight(o) { - var regex; - o = _.mixin({}, defaults, o); - if (!o.node || !o.pattern) { - return; - } - o.pattern = _.isArray(o.pattern) ? o.pattern : [ o.pattern ]; - regex = getRegex(o.pattern, o.caseSensitive, o.wordsOnly); - traverse(o.node, hightlightTextNode); - function hightlightTextNode(textNode) { - var match, patternNode; - if (match = regex.exec(textNode.data)) { - wrapperNode = doc.createElement(o.tagName); - o.className && (wrapperNode.className = o.className); - patternNode = textNode.splitText(match.index); - patternNode.splitText(match[0].length); - wrapperNode.appendChild(patternNode.cloneNode(true)); - textNode.parentNode.replaceChild(wrapperNode, patternNode); - } - return !!match; - } - function traverse(el, hightlightTextNode) { - var childNode, TEXT_NODE_TYPE = 3; - for (var i = 0; i < el.childNodes.length; i++) { - childNode = el.childNodes[i]; - if (childNode.nodeType === TEXT_NODE_TYPE) { - i += hightlightTextNode(childNode) ? 1 : 0; - } else { - traverse(childNode, hightlightTextNode); - } - } - } - }; - function getRegex(patterns, caseSensitive, wordsOnly) { - var escapedPatterns = [], regexStr; - for (var i = 0; i < patterns.length; i++) { - escapedPatterns.push(_.escapeRegExChars(patterns[i])); + var defaults = { + node: null, + pattern: null, + tagName: 'strong', + className: null, + wordsOnly: false, + caseSensitive: false + }; + return function hightlight(o) { + var regex; + o = _.mixin({}, defaults, o); + if (!o.node || !o.pattern) { + return; + } + o.pattern = _.isArray(o.pattern) ? o.pattern : [ o.pattern ]; + regex = getRegex(o.pattern, o.caseSensitive, o.wordsOnly); + traverse(o.node, hightlightTextNode); + function hightlightTextNode(textNode) { + var match, patternNode; + if (match = regex.exec(textNode.data)) { + wrapperNode = doc.createElement(o.tagName); + o.className && (wrapperNode.className = o.className); + patternNode = textNode.splitText(match.index); + patternNode.splitText(match[0].length); + wrapperNode.appendChild(patternNode.cloneNode(true)); + textNode.parentNode.replaceChild(wrapperNode, patternNode); + } + return !!match; + } + function traverse(el, hightlightTextNode) { + var childNode, TEXT_NODE_TYPE = 3; + for (var i = 0; i < el.childNodes.length; i++) { + childNode = el.childNodes[i]; + if (childNode.nodeType === TEXT_NODE_TYPE) { + i += hightlightTextNode(childNode) ? 1 : 0; + } else { + traverse(childNode, hightlightTextNode); } - regexStr = wordsOnly ? "\\b(" + escapedPatterns.join("|") + ")\\b" : "(" + escapedPatterns.join("|") + ")"; - return caseSensitive ? new RegExp(regexStr) : new RegExp(regexStr, "i"); + } } + }; + function getRegex(patterns, caseSensitive, wordsOnly) { + var escapedPatterns = [], regexStr; + for (var i = 0; i < patterns.length; i++) { + escapedPatterns.push(_.escapeRegExChars(patterns[i])); + } + regexStr = wordsOnly ? '\\b(' + escapedPatterns.join('|') + ')\\b' : '(' + escapedPatterns.join('|') + ')'; + return caseSensitive ? new RegExp(regexStr) : new RegExp(regexStr, 'i'); + } }(window.document); var Input = function() { - var specialKeyCodeMap; - specialKeyCodeMap = { - 9: "tab", - 27: "esc", - 37: "left", - 39: "right", - 13: "enter", - 38: "up", - 40: "down" - }; - function Input(o) { - var that = this, onBlur, onFocus, onKeydown, onInput; - o = o || {}; - if (!o.input) { - $.error("input is missing"); - } - onBlur = _.bind(this._onBlur, this); - onFocus = _.bind(this._onFocus, this); - onKeydown = _.bind(this._onKeydown, this); - onInput = _.bind(this._onInput, this); - this.$hint = $(o.hint); - this.$input = $(o.input).on("blur.tt", onBlur).on("focus.tt", onFocus).on("keydown.tt", onKeydown); - if (this.$hint.length === 0) { - this.setHint = this.getHint = this.clearHint = this.clearHintIfInvalid = _.noop; - } - if (!_.isMsie()) { - this.$input.on("input.tt", onInput); - } else { - this.$input.on("keydown.tt keypress.tt cut.tt paste.tt", function($e) { - if (specialKeyCodeMap[$e.which || $e.keyCode]) { - return; - } - _.defer(_.bind(that._onInput, that, $e)); - }); - } - this.query = this.$input.val(); - this.$overflowHelper = buildOverflowHelper(this.$input); + var specialKeyCodeMap; + specialKeyCodeMap = { + 9: 'tab', + 27: 'esc', + 37: 'left', + 39: 'right', + 13: 'enter', + 38: 'up', + 40: 'down' + }; + function Input(o) { + var that = this, onBlur, onFocus, onKeydown, onInput; + o = o || {}; + if (!o.input) { + $.error('input is missing'); } - Input.normalizeQuery = function(str) { - return (str || "").replace(/^\s*/g, "").replace(/\s{2,}/g, " "); - }; - _.mixin(Input.prototype, EventEmitter, { - _onBlur: function onBlur() { - this.resetInputValue(); - this.trigger("blurred"); - }, - _onFocus: function onFocus() { - this.trigger("focused"); - }, - _onKeydown: function onKeydown($e) { - var keyName = specialKeyCodeMap[$e.which || $e.keyCode]; - this._managePreventDefault(keyName, $e); - if (keyName && this._shouldTrigger(keyName, $e)) { - this.trigger(keyName + "Keyed", $e); - } - }, - _onInput: function onInput() { - this._checkInputValue(); - }, - _managePreventDefault: function managePreventDefault(keyName, $e) { - var preventDefault, hintValue, inputValue; - switch (keyName) { - case "tab": - hintValue = this.getHint(); - inputValue = this.getInputValue(); - preventDefault = hintValue && hintValue !== inputValue && !withModifier($e); - break; - - case "up": - case "down": - preventDefault = !withModifier($e); - break; - - default: - preventDefault = false; - } - preventDefault && $e.preventDefault(); - }, - _shouldTrigger: function shouldTrigger(keyName, $e) { - var trigger; - switch (keyName) { - case "tab": - trigger = !withModifier($e); - break; - - default: - trigger = true; - } - return trigger; - }, - _checkInputValue: function checkInputValue() { - var inputValue, areEquivalent, hasDifferentWhitespace; - inputValue = this.getInputValue(); - areEquivalent = areQueriesEquivalent(inputValue, this.query); - hasDifferentWhitespace = areEquivalent ? this.query.length !== inputValue.length : false; - if (!areEquivalent) { - this.trigger("queryChanged", this.query = inputValue); - } else if (hasDifferentWhitespace) { - this.trigger("whitespaceChanged", this.query); - } - }, - focus: function focus() { - this.$input.focus(); - }, - blur: function blur() { - this.$input.blur(); - }, - getQuery: function getQuery() { - return this.query; - }, - setQuery: function setQuery(query) { - this.query = query; - }, - getInputValue: function getInputValue() { - return this.$input.val(); - }, - setInputValue: function setInputValue(value, silent) { - this.$input.val(value); - silent ? this.clearHint() : this._checkInputValue(); - }, - resetInputValue: function resetInputValue() { - this.setInputValue(this.query, true); - }, - getHint: function getHint() { - return this.$hint.val(); - }, - setHint: function setHint(value) { - this.$hint.val(value); - }, - clearHint: function clearHint() { - this.setHint(""); - }, - clearHintIfInvalid: function clearHintIfInvalid() { - var val, hint, valIsPrefixOfHint, isValid; - val = this.getInputValue(); - hint = this.getHint(); - valIsPrefixOfHint = val !== hint && hint.indexOf(val) === 0; - isValid = val !== "" && valIsPrefixOfHint && !this.hasOverflow(); - !isValid && this.clearHint(); - }, - getLanguageDirection: function getLanguageDirection() { - return (this.$input.css("direction") || "ltr").toLowerCase(); - }, - hasOverflow: function hasOverflow() { - var constraint = this.$input.width() - 2; - this.$overflowHelper.text(this.getInputValue()); - return this.$overflowHelper.width() >= constraint; - }, - isCursorAtEnd: function() { - var valueLength, selectionStart, range; - valueLength = this.$input.val().length; - selectionStart = this.$input[0].selectionStart; - if (_.isNumber(selectionStart)) { - return selectionStart === valueLength; - } else if (document.selection) { - range = document.selection.createRange(); - range.moveStart("character", -valueLength); - return valueLength === range.text.length; - } - return true; - }, - destroy: function destroy() { - this.$hint.off(".tt"); - this.$input.off(".tt"); - this.$hint = this.$input = this.$overflowHelper = null; - } - }); - return Input; - function buildOverflowHelper($input) { - return $('').css({ - position: "absolute", - visibility: "hidden", - whiteSpace: "pre", - fontFamily: $input.css("font-family"), - fontSize: $input.css("font-size"), - fontStyle: $input.css("font-style"), - fontVariant: $input.css("font-variant"), - fontWeight: $input.css("font-weight"), - wordSpacing: $input.css("word-spacing"), - letterSpacing: $input.css("letter-spacing"), - textIndent: $input.css("text-indent"), - textRendering: $input.css("text-rendering"), - textTransform: $input.css("text-transform") - }).insertAfter($input); + onBlur = _.bind(this._onBlur, this); + onFocus = _.bind(this._onFocus, this); + onKeydown = _.bind(this._onKeydown, this); + onInput = _.bind(this._onInput, this); + this.$hint = $(o.hint); + this.$input = $(o.input).on('blur.tt', onBlur).on('focus.tt', onFocus).on('keydown.tt', onKeydown); + if (this.$hint.length === 0) { + this.setHint = this.getHint = this.clearHint = this.clearHintIfInvalid = _.noop; } - function areQueriesEquivalent(a, b) { - return Input.normalizeQuery(a) === Input.normalizeQuery(b); + if (!_.isMsie()) { + this.$input.on('input.tt', onInput); + } else { + this.$input.on('keydown.tt keypress.tt cut.tt paste.tt', function($e) { + if (specialKeyCodeMap[$e.which || $e.keyCode]) { + return; + } + _.defer(_.bind(that._onInput, that, $e)); + }); } - function withModifier($e) { - return $e.altKey || $e.ctrlKey || $e.metaKey || $e.shiftKey; + this.query = this.$input.val(); + this.$overflowHelper = buildOverflowHelper(this.$input); + } + Input.normalizeQuery = function(str) { + return (str || '').replace(/^\s*/g, '').replace(/\s{2,}/g, ' '); + }; + _.mixin(Input.prototype, EventEmitter, { + _onBlur: function onBlur() { + this.resetInputValue(); + this.trigger('blurred'); + }, + _onFocus: function onFocus() { + this.trigger('focused'); + }, + _onKeydown: function onKeydown($e) { + var keyName = specialKeyCodeMap[$e.which || $e.keyCode]; + this._managePreventDefault(keyName, $e); + if (keyName && this._shouldTrigger(keyName, $e)) { + this.trigger(keyName + 'Keyed', $e); + } + }, + _onInput: function onInput() { + this._checkInputValue(); + }, + _managePreventDefault: function managePreventDefault(keyName, $e) { + var preventDefault, hintValue, inputValue; + switch (keyName) { + case 'tab': + hintValue = this.getHint(); + inputValue = this.getInputValue(); + preventDefault = hintValue && hintValue !== inputValue && !withModifier($e); + break; + + case 'up': + case 'down': + preventDefault = !withModifier($e); + break; + + default: + preventDefault = false; + } + preventDefault && $e.preventDefault(); + }, + _shouldTrigger: function shouldTrigger(keyName, $e) { + var trigger; + switch (keyName) { + case 'tab': + trigger = !withModifier($e); + break; + + default: + trigger = true; + } + return trigger; + }, + _checkInputValue: function checkInputValue() { + var inputValue, areEquivalent, hasDifferentWhitespace; + inputValue = this.getInputValue(); + areEquivalent = areQueriesEquivalent(inputValue, this.query); + hasDifferentWhitespace = areEquivalent ? this.query.length !== inputValue.length : false; + if (!areEquivalent) { + this.trigger('queryChanged', this.query = inputValue); + } else if (hasDifferentWhitespace) { + this.trigger('whitespaceChanged', this.query); + } + }, + focus: function focus() { + this.$input.focus(); + }, + blur: function blur() { + this.$input.blur(); + }, + getQuery: function getQuery() { + return this.query; + }, + setQuery: function setQuery(query) { + this.query = query; + }, + getInputValue: function getInputValue() { + return this.$input.val(); + }, + setInputValue: function setInputValue(value, silent) { + this.$input.val(value); + silent ? this.clearHint() : this._checkInputValue(); + }, + resetInputValue: function resetInputValue() { + this.setInputValue(this.query, true); + }, + getHint: function getHint() { + return this.$hint.val(); + }, + setHint: function setHint(value) { + this.$hint.val(value); + }, + clearHint: function clearHint() { + this.setHint(''); + }, + clearHintIfInvalid: function clearHintIfInvalid() { + var val, hint, valIsPrefixOfHint, isValid; + val = this.getInputValue(); + hint = this.getHint(); + valIsPrefixOfHint = val !== hint && hint.indexOf(val) === 0; + isValid = val !== '' && valIsPrefixOfHint && !this.hasOverflow(); + !isValid && this.clearHint(); + }, + getLanguageDirection: function getLanguageDirection() { + return (this.$input.css('direction') || 'ltr').toLowerCase(); + }, + hasOverflow: function hasOverflow() { + var constraint = this.$input.width() - 2; + this.$overflowHelper.text(this.getInputValue()); + return this.$overflowHelper.width() >= constraint; + }, + isCursorAtEnd: function() { + var valueLength, selectionStart, range; + valueLength = this.$input.val().length; + selectionStart = this.$input[0].selectionStart; + if (_.isNumber(selectionStart)) { + return selectionStart === valueLength; + } else if (document.selection) { + range = document.selection.createRange(); + range.moveStart('character', -valueLength); + return valueLength === range.text.length; + } + return true; + }, + destroy: function destroy() { + this.$hint.off('.tt'); + this.$input.off('.tt'); + this.$hint = this.$input = this.$overflowHelper = null; } + }); + return Input; + function buildOverflowHelper($input) { + return $('').css({ + position: 'absolute', + visibility: 'hidden', + whiteSpace: 'pre', + fontFamily: $input.css('font-family'), + fontSize: $input.css('font-size'), + fontStyle: $input.css('font-style'), + fontVariant: $input.css('font-variant'), + fontWeight: $input.css('font-weight'), + wordSpacing: $input.css('word-spacing'), + letterSpacing: $input.css('letter-spacing'), + textIndent: $input.css('text-indent'), + textRendering: $input.css('text-rendering'), + textTransform: $input.css('text-transform') + }).insertAfter($input); + } + function areQueriesEquivalent(a, b) { + return Input.normalizeQuery(a) === Input.normalizeQuery(b); + } + function withModifier($e) { + return $e.altKey || $e.ctrlKey || $e.metaKey || $e.shiftKey; + } }(); var Dataset = function() { - var datasetKey = "ttDataset", valueKey = "ttValue", datumKey = "ttDatum"; - function Dataset(o) { - o = o || {}; - o.templates = o.templates || {}; - if (!o.source) { - $.error("missing source"); - } - if (o.name && !isValidName(o.name)) { - $.error("invalid dataset name: " + o.name); - } - this.query = null; - this.highlight = !!o.highlight; - this.name = o.name || _.getUniqueId(); - this.source = o.source; - this.displayFn = getDisplayFn(o.display || o.displayKey); - this.templates = getTemplates(o.templates, this.displayFn); - this.$el = $(html.dataset.replace("%CLASS%", this.name)); + var datasetKey = 'ttDataset', valueKey = 'ttValue', datumKey = 'ttDatum'; + function Dataset(o) { + o = o || {}; + o.templates = o.templates || {}; + if (!o.source) { + $.error('missing source'); } - Dataset.extractDatasetName = function extractDatasetName(el) { - return $(el).data(datasetKey); - }; - Dataset.extractValue = function extractDatum(el) { - return $(el).data(valueKey); - }; - Dataset.extractDatum = function extractDatum(el) { - return $(el).data(datumKey); - }; - _.mixin(Dataset.prototype, EventEmitter, { - _render: function render(query, suggestions) { - if (!this.$el) { - return; - } - var that = this, hasSuggestions; - this.$el.empty(); - hasSuggestions = suggestions && suggestions.length; - if (!hasSuggestions && this.templates.empty) { - this.$el.html(getEmptyHtml()).prepend(that.templates.header ? getHeaderHtml() : null).append(that.templates.footer ? getFooterHtml() : null); - } else if (hasSuggestions) { - this.$el.html(getSuggestionsHtml()).prepend(that.templates.header ? getHeaderHtml() : null).append(that.templates.footer ? getFooterHtml() : null); - } - this.trigger("rendered"); - function getEmptyHtml() { - return that.templates.empty({ - query: query, - isEmpty: true - }); - } - function getSuggestionsHtml() { - var $suggestions, nodes; - $suggestions = $(html.suggestions).css(css.suggestions); - nodes = _.map(suggestions, getSuggestionNode); - $suggestions.append.apply($suggestions, nodes); - that.highlight && highlight({ - node: $suggestions[0], - pattern: query - }); - return $suggestions; - function getSuggestionNode(suggestion) { - var $el; - $el = $(html.suggestion).append(that.templates.suggestion(suggestion)).data(datasetKey, that.name).data(valueKey, that.displayFn(suggestion)).data(datumKey, suggestion); - $el.children().each(function() { - $(this).css(css.suggestionChild); - }); - return $el; - } - } - function getHeaderHtml() { - return that.templates.header({ - query: query, - isEmpty: !hasSuggestions - }); - } - function getFooterHtml() { - return that.templates.footer({ - query: query, - isEmpty: !hasSuggestions - }); - } - }, - getRoot: function getRoot() { - return this.$el; - }, - update: function update(query) { - var that = this; - this.query = query; - this.canceled = false; - this.source(query, render); - function render(suggestions) { - if (!that.canceled && query === that.query) { - that._render(query, suggestions); - } - } - }, - cancel: function cancel() { - this.canceled = true; - }, - clear: function clear() { - this.cancel(); - this.$el.empty(); - this.trigger("rendered"); - }, - isEmpty: function isEmpty() { - return this.$el.is(":empty"); - }, - destroy: function destroy() { - this.$el = null; + if (o.name && !isValidName(o.name)) { + $.error('invalid dataset name: ' + o.name); + } + this.query = null; + this.highlight = !!o.highlight; + this.name = o.name || _.getUniqueId(); + this.source = o.source; + this.displayFn = getDisplayFn(o.display || o.displayKey); + this.templates = getTemplates(o.templates, this.displayFn); + this.$el = $(html.dataset.replace('%CLASS%', this.name)); + } + Dataset.extractDatasetName = function extractDatasetName(el) { + return $(el).data(datasetKey); + }; + Dataset.extractValue = function extractDatum(el) { + return $(el).data(valueKey); + }; + Dataset.extractDatum = function extractDatum(el) { + return $(el).data(datumKey); + }; + _.mixin(Dataset.prototype, EventEmitter, { + _render: function render(query, suggestions) { + if (!this.$el) { + return; + } + var that = this, hasSuggestions; + this.$el.empty(); + hasSuggestions = suggestions && suggestions.length; + if (!hasSuggestions && this.templates.empty) { + this.$el.html(getEmptyHtml()).prepend(that.templates.header ? getHeaderHtml() : null).append(that.templates.footer ? getFooterHtml() : null); + } else if (hasSuggestions) { + this.$el.html(getSuggestionsHtml()).prepend(that.templates.header ? getHeaderHtml() : null).append(that.templates.footer ? getFooterHtml() : null); + } + this.trigger('rendered'); + function getEmptyHtml() { + return that.templates.empty({ + query: query, + isEmpty: true + }); + } + function getSuggestionsHtml() { + var $suggestions, nodes; + $suggestions = $(html.suggestions).css(css.suggestions); + nodes = _.map(suggestions, getSuggestionNode); + $suggestions.append.apply($suggestions, nodes); + that.highlight && highlight({ + node: $suggestions[0], + pattern: query + }); + return $suggestions; + function getSuggestionNode(suggestion) { + var $el; + $el = $(html.suggestion).append(that.templates.suggestion(suggestion)).data(datasetKey, that.name).data(valueKey, that.displayFn(suggestion)).data(datumKey, suggestion); + $el.children().each(function() { + $(this).css(css.suggestionChild); + }); + return $el; } - }); - return Dataset; - function getDisplayFn(display) { - display = display || "value"; - return _.isFunction(display) ? display : displayFn; - function displayFn(obj) { - return obj[display]; + } + function getHeaderHtml() { + return that.templates.header({ + query: query, + isEmpty: !hasSuggestions + }); + } + function getFooterHtml() { + return that.templates.footer({ + query: query, + isEmpty: !hasSuggestions + }); + } + }, + getRoot: function getRoot() { + return this.$el; + }, + update: function update(query) { + var that = this; + this.query = query; + this.canceled = false; + this.source(query, render); + function render(suggestions) { + if (!that.canceled && query === that.query) { + that._render(query, suggestions); } + } + }, + cancel: function cancel() { + this.canceled = true; + }, + clear: function clear() { + this.cancel(); + this.$el.empty(); + this.trigger('rendered'); + }, + isEmpty: function isEmpty() { + return this.$el.is(':empty'); + }, + destroy: function destroy() { + this.$el = null; } - function getTemplates(templates, displayFn) { - return { - empty: templates.empty && _.templatify(templates.empty), - header: templates.header && _.templatify(templates.header), - footer: templates.footer && _.templatify(templates.footer), - suggestion: templates.suggestion || suggestionTemplate - }; - function suggestionTemplate(context) { - return "" + displayFn(context) + "
"; - } + }); + return Dataset; + function getDisplayFn(display) { + display = display || 'value'; + return _.isFunction(display) ? display : displayFn; + function displayFn(obj) { + return obj[display]; } - function isValidName(str) { - return /^[_a-zA-Z0-9-]+$/.test(str); + } + function getTemplates(templates, displayFn) { + return { + empty: templates.empty && _.templatify(templates.empty), + header: templates.header && _.templatify(templates.header), + footer: templates.footer && _.templatify(templates.footer), + suggestion: templates.suggestion || suggestionTemplate + }; + function suggestionTemplate(context) { + return '' + displayFn(context) + '
'; } + } + function isValidName(str) { + return /^[_a-zA-Z0-9-]+$/.test(str); + } }(); var Dropdown = function() { - function Dropdown(o) { - var that = this, onSuggestionClick, onSuggestionMouseEnter, onSuggestionMouseLeave; - o = o || {}; - if (!o.menu) { - $.error("menu is required"); - } - this.isOpen = false; - this.isEmpty = true; - this.datasets = _.map(o.datasets, initializeDataset); - onSuggestionClick = _.bind(this._onSuggestionClick, this); - onSuggestionMouseEnter = _.bind(this._onSuggestionMouseEnter, this); - onSuggestionMouseLeave = _.bind(this._onSuggestionMouseLeave, this); - this.$menu = $(o.menu).on("click.tt", ".tt-suggestion", onSuggestionClick).on("mouseenter.tt", ".tt-suggestion", onSuggestionMouseEnter).on("mouseleave.tt", ".tt-suggestion", onSuggestionMouseLeave); - _.each(this.datasets, function(dataset) { - that.$menu.append(dataset.getRoot()); - dataset.onSync("rendered", that._onRendered, that); - }); + function Dropdown(o) { + var that = this, onSuggestionClick, onSuggestionMouseEnter, onSuggestionMouseLeave; + o = o || {}; + if (!o.menu) { + $.error('menu is required'); } - _.mixin(Dropdown.prototype, EventEmitter, { - _onSuggestionClick: function onSuggestionClick($e) { - this.trigger("suggestionClicked", $($e.currentTarget)); - }, - _onSuggestionMouseEnter: function onSuggestionMouseEnter($e) { - this._removeCursor(); - this._setCursor($($e.currentTarget), true); - }, - _onSuggestionMouseLeave: function onSuggestionMouseLeave() { - this._removeCursor(); - }, - _onRendered: function onRendered() { - this.isEmpty = _.every(this.datasets, isDatasetEmpty); - this.isEmpty ? this._hide() : this.isOpen && this._show(); - this.trigger("datasetRendered"); - function isDatasetEmpty(dataset) { - return dataset.isEmpty(); - } - }, - _hide: function() { - this.$menu.hide(); - }, - _show: function() { - this.$menu.css("display", "block"); - }, - _getSuggestions: function getSuggestions() { - return this.$menu.find(".tt-suggestion"); - }, - _getCursor: function getCursor() { - return this.$menu.find(".tt-cursor").first(); - }, - _setCursor: function setCursor($el, silent) { - $el.first().addClass("tt-cursor"); - !silent && this.trigger("cursorMoved"); - }, - _removeCursor: function removeCursor() { - this._getCursor().removeClass("tt-cursor"); - }, - _moveCursor: function moveCursor(increment) { - var $suggestions, $oldCursor, newCursorIndex, $newCursor; - if (!this.isOpen) { - return; - } - $oldCursor = this._getCursor(); - $suggestions = this._getSuggestions(); - this._removeCursor(); - newCursorIndex = $suggestions.index($oldCursor) + increment; - newCursorIndex = (newCursorIndex + 1) % ($suggestions.length + 1) - 1; - if (newCursorIndex === -1) { - this.trigger("cursorRemoved"); - return; - } else if (newCursorIndex < -1) { - newCursorIndex = $suggestions.length - 1; - } - this._setCursor($newCursor = $suggestions.eq(newCursorIndex)); - this._ensureVisible($newCursor); - }, - _ensureVisible: function ensureVisible($el) { - var elTop, elBottom, menuScrollTop, menuHeight; - elTop = $el.position().top; - elBottom = elTop + $el.outerHeight(true); - menuScrollTop = this.$menu.scrollTop(); - menuHeight = this.$menu.height() + parseInt(this.$menu.css("paddingTop"), 10) + parseInt(this.$menu.css("paddingBottom"), 10); - if (elTop < 0) { - this.$menu.scrollTop(menuScrollTop + elTop); - } else if (menuHeight < elBottom) { - this.$menu.scrollTop(menuScrollTop + (elBottom - menuHeight)); - } - }, - close: function close() { - if (this.isOpen) { - this.isOpen = false; - this._removeCursor(); - this._hide(); - this.trigger("closed"); - } - }, - open: function open() { - if (!this.isOpen) { - this.isOpen = true; - !this.isEmpty && this._show(); - this.trigger("opened"); - } - }, - setLanguageDirection: function setLanguageDirection(dir) { - this.$menu.css(dir === "ltr" ? css.ltr : css.rtl); - }, - moveCursorUp: function moveCursorUp() { - this._moveCursor(-1); - }, - moveCursorDown: function moveCursorDown() { - this._moveCursor(+1); - }, - getDatumForSuggestion: function getDatumForSuggestion($el) { - var datum = null; - if ($el.length) { - datum = { - raw: Dataset.extractDatum($el), - value: Dataset.extractValue($el), - datasetName: Dataset.extractDatasetName($el) - }; - } - return datum; - }, - getDatumForCursor: function getDatumForCursor() { - return this.getDatumForSuggestion(this._getCursor().first()); - }, - getDatumForTopSuggestion: function getDatumForTopSuggestion() { - return this.getDatumForSuggestion(this._getSuggestions().first()); - }, - update: function update(query) { - _.each(this.datasets, updateDataset); - function updateDataset(dataset) { - dataset.update(query); - } - }, - empty: function empty() { - _.each(this.datasets, clearDataset); - this.isEmpty = true; - function clearDataset(dataset) { - dataset.clear(); - } - }, - isVisible: function isVisible() { - return this.isOpen && !this.isEmpty; - }, - destroy: function destroy() { - this.$menu.off(".tt"); - this.$menu = null; - _.each(this.datasets, destroyDataset); - function destroyDataset(dataset) { - dataset.destroy(); - } - } + this.isOpen = false; + this.isEmpty = true; + this.datasets = _.map(o.datasets, initializeDataset); + onSuggestionClick = _.bind(this._onSuggestionClick, this); + onSuggestionMouseEnter = _.bind(this._onSuggestionMouseEnter, this); + onSuggestionMouseLeave = _.bind(this._onSuggestionMouseLeave, this); + this.$menu = $(o.menu).on('click.tt', '.tt-suggestion', onSuggestionClick).on('mouseenter.tt', '.tt-suggestion', onSuggestionMouseEnter).on('mouseleave.tt', '.tt-suggestion', onSuggestionMouseLeave); + _.each(this.datasets, function(dataset) { + that.$menu.append(dataset.getRoot()); + dataset.onSync('rendered', that._onRendered, that); }); - return Dropdown; - function initializeDataset(oDataset) { - return new Dataset(oDataset); + } + _.mixin(Dropdown.prototype, EventEmitter, { + _onSuggestionClick: function onSuggestionClick($e) { + this.trigger('suggestionClicked', $($e.currentTarget)); + }, + _onSuggestionMouseEnter: function onSuggestionMouseEnter($e) { + this._removeCursor(); + this._setCursor($($e.currentTarget), true); + }, + _onSuggestionMouseLeave: function onSuggestionMouseLeave() { + this._removeCursor(); + }, + _onRendered: function onRendered() { + this.isEmpty = _.every(this.datasets, isDatasetEmpty); + this.isEmpty ? this._hide() : this.isOpen && this._show(); + this.trigger('datasetRendered'); + function isDatasetEmpty(dataset) { + return dataset.isEmpty(); + } + }, + _hide: function() { + this.$menu.hide(); + }, + _show: function() { + this.$menu.css('display', 'block'); + }, + _getSuggestions: function getSuggestions() { + return this.$menu.find('.tt-suggestion'); + }, + _getCursor: function getCursor() { + return this.$menu.find('.tt-cursor').first(); + }, + _setCursor: function setCursor($el, silent) { + $el.first().addClass('tt-cursor'); + !silent && this.trigger('cursorMoved'); + }, + _removeCursor: function removeCursor() { + this._getCursor().removeClass('tt-cursor'); + }, + _moveCursor: function moveCursor(increment) { + var $suggestions, $oldCursor, newCursorIndex, $newCursor; + if (!this.isOpen) { + return; + } + $oldCursor = this._getCursor(); + $suggestions = this._getSuggestions(); + this._removeCursor(); + newCursorIndex = $suggestions.index($oldCursor) + increment; + newCursorIndex = (newCursorIndex + 1) % ($suggestions.length + 1) - 1; + if (newCursorIndex === -1) { + this.trigger('cursorRemoved'); + return; + } else if (newCursorIndex < -1) { + newCursorIndex = $suggestions.length - 1; + } + this._setCursor($newCursor = $suggestions.eq(newCursorIndex)); + this._ensureVisible($newCursor); + }, + _ensureVisible: function ensureVisible($el) { + var elTop, elBottom, menuScrollTop, menuHeight; + elTop = $el.position().top; + elBottom = elTop + $el.outerHeight(true); + menuScrollTop = this.$menu.scrollTop(); + menuHeight = this.$menu.height() + parseInt(this.$menu.css('paddingTop'), 10) + parseInt(this.$menu.css('paddingBottom'), 10); + if (elTop < 0) { + this.$menu.scrollTop(menuScrollTop + elTop); + } else if (menuHeight < elBottom) { + this.$menu.scrollTop(menuScrollTop + (elBottom - menuHeight)); + } + }, + close: function close() { + if (this.isOpen) { + this.isOpen = false; + this._removeCursor(); + this._hide(); + this.trigger('closed'); + } + }, + open: function open() { + if (!this.isOpen) { + this.isOpen = true; + !this.isEmpty && this._show(); + this.trigger('opened'); + } + }, + setLanguageDirection: function setLanguageDirection(dir) { + this.$menu.css(dir === 'ltr' ? css.ltr : css.rtl); + }, + moveCursorUp: function moveCursorUp() { + this._moveCursor(-1); + }, + moveCursorDown: function moveCursorDown() { + this._moveCursor(+1); + }, + getDatumForSuggestion: function getDatumForSuggestion($el) { + var datum = null; + if ($el.length) { + datum = { + raw: Dataset.extractDatum($el), + value: Dataset.extractValue($el), + datasetName: Dataset.extractDatasetName($el) + }; + } + return datum; + }, + getDatumForCursor: function getDatumForCursor() { + return this.getDatumForSuggestion(this._getCursor().first()); + }, + getDatumForTopSuggestion: function getDatumForTopSuggestion() { + return this.getDatumForSuggestion(this._getSuggestions().first()); + }, + update: function update(query) { + _.each(this.datasets, updateDataset); + function updateDataset(dataset) { + dataset.update(query); + } + }, + empty: function empty() { + _.each(this.datasets, clearDataset); + this.isEmpty = true; + function clearDataset(dataset) { + dataset.clear(); + } + }, + isVisible: function isVisible() { + return this.isOpen && !this.isEmpty; + }, + destroy: function destroy() { + this.$menu.off('.tt'); + this.$menu = null; + _.each(this.datasets, destroyDataset); + function destroyDataset(dataset) { + dataset.destroy(); + } } + }); + return Dropdown; + function initializeDataset(oDataset) { + return new Dataset(oDataset); + } }(); var Typeahead = function() { - var attrsKey = "ttAttrs"; - function Typeahead(o) { - var $menu, $input, $hint; - o = o || {}; - if (!o.input) { - $.error("missing input"); - } - this.isActivated = false; - this.autoselect = !!o.autoselect; - this.minLength = _.isNumber(o.minLength) ? o.minLength : 1; - this.$node = buildDomStructure(o.input, o.withHint); - $menu = this.$node.find(".tt-dropdown-menu"); - $input = this.$node.find(".tt-input"); - $hint = this.$node.find(".tt-hint"); - $input.on("blur.tt", function($e) { - var active, isActive, hasActive; - active = document.activeElement; - isActive = $menu.is(active); - hasActive = $menu.has(active).length > 0; - if (_.isMsie() && (isActive || hasActive)) { - $e.preventDefault(); - $e.stopImmediatePropagation(); - _.defer(function() { - $input.focus(); - }); - } - }); - $menu.on("mousedown.tt", function($e) { - $e.preventDefault(); - }); - this.eventBus = o.eventBus || new EventBus({ - el: $input - }); - this.dropdown = new Dropdown({ - menu: $menu, - datasets: o.datasets - }).onSync("suggestionClicked", this._onSuggestionClicked, this).onSync("cursorMoved", this._onCursorMoved, this).onSync("cursorRemoved", this._onCursorRemoved, this).onSync("opened", this._onOpened, this).onSync("closed", this._onClosed, this).onAsync("datasetRendered", this._onDatasetRendered, this); - this.input = new Input({ - input: $input, - hint: $hint - }).onSync("focused", this._onFocused, this).onSync("blurred", this._onBlurred, this).onSync("enterKeyed", this._onEnterKeyed, this).onSync("tabKeyed", this._onTabKeyed, this).onSync("escKeyed", this._onEscKeyed, this).onSync("upKeyed", this._onUpKeyed, this).onSync("downKeyed", this._onDownKeyed, this).onSync("leftKeyed", this._onLeftKeyed, this).onSync("rightKeyed", this._onRightKeyed, this).onSync("queryChanged", this._onQueryChanged, this).onSync("whitespaceChanged", this._onWhitespaceChanged, this); - this._setLanguageDirection(); + var attrsKey = 'ttAttrs'; + function Typeahead(o) { + var $menu, $input, $hint; + o = o || {}; + if (!o.input) { + $.error('missing input'); } - _.mixin(Typeahead.prototype, { - _onSuggestionClicked: function onSuggestionClicked(type, $el) { - var datum; - if (datum = this.dropdown.getDatumForSuggestion($el)) { - this._select(datum); - } - }, - _onCursorMoved: function onCursorMoved() { - var datum = this.dropdown.getDatumForCursor(); - this.input.setInputValue(datum.value, true); - this.eventBus.trigger("cursorchanged", datum.raw, datum.datasetName); - }, - _onCursorRemoved: function onCursorRemoved() { - this.input.resetInputValue(); - this._updateHint(); - }, - _onDatasetRendered: function onDatasetRendered() { - this._updateHint(); - }, - _onOpened: function onOpened() { - this._updateHint(); - this.eventBus.trigger("opened"); - }, - _onClosed: function onClosed() { - this.input.clearHint(); - this.eventBus.trigger("closed"); - }, - _onFocused: function onFocused() { - this.isActivated = true; - this.dropdown.open(); - }, - _onBlurred: function onBlurred() { - this.isActivated = false; - this.dropdown.empty(); - this.dropdown.close(); - this.setVal("", true); //LM - }, - _onEnterKeyed: function onEnterKeyed(type, $e) { - var cursorDatum, topSuggestionDatum; - cursorDatum = this.dropdown.getDatumForCursor(); - topSuggestionDatum = this.dropdown.getDatumForTopSuggestion(); - if (cursorDatum) { - this._select(cursorDatum); - $e.preventDefault(); - } else if (this.autoselect && topSuggestionDatum) { - this._select(topSuggestionDatum); - $e.preventDefault(); - } - }, - _onTabKeyed: function onTabKeyed(type, $e) { - var datum; - if (datum = this.dropdown.getDatumForCursor()) { - this._select(datum); - $e.preventDefault(); - } else { - this._autocomplete(true); - } - }, - _onEscKeyed: function onEscKeyed() { - this.dropdown.close(); - this.input.resetInputValue(); - }, - _onUpKeyed: function onUpKeyed() { - var query = this.input.getQuery(); - this.dropdown.isEmpty && query.length >= this.minLength ? this.dropdown.update(query) : this.dropdown.moveCursorUp(); - this.dropdown.open(); - }, - _onDownKeyed: function onDownKeyed() { - var query = this.input.getQuery(); - this.dropdown.isEmpty && query.length >= this.minLength ? this.dropdown.update(query) : this.dropdown.moveCursorDown(); - this.dropdown.open(); - }, - _onLeftKeyed: function onLeftKeyed() { - this.dir === "rtl" && this._autocomplete(); - }, - _onRightKeyed: function onRightKeyed() { - this.dir === "ltr" && this._autocomplete(); - }, - _onQueryChanged: function onQueryChanged(e, query) { - this.input.clearHintIfInvalid(); - query.length >= this.minLength ? this.dropdown.update(query) : this.dropdown.empty(); - this.dropdown.open(); - this._setLanguageDirection(); - }, - _onWhitespaceChanged: function onWhitespaceChanged() { - this._updateHint(); - this.dropdown.open(); - }, - _setLanguageDirection: function setLanguageDirection() { - var dir; - if (this.dir !== (dir = this.input.getLanguageDirection())) { - this.dir = dir; - this.$node.css("direction", dir); - this.dropdown.setLanguageDirection(dir); - } - }, - _updateHint: function updateHint() { - var datum, val, query, escapedQuery, frontMatchRegEx, match; - datum = this.dropdown.getDatumForTopSuggestion(); - if (datum && this.dropdown.isVisible() && !this.input.hasOverflow()) { - val = this.input.getInputValue(); - query = Input.normalizeQuery(val); - escapedQuery = _.escapeRegExChars(query); - frontMatchRegEx = new RegExp("^(?:" + escapedQuery + ")(.+$)", "i"); - match = frontMatchRegEx.exec(datum.value); - match ? this.input.setHint(val + match[1]) : this.input.clearHint(); - } else { - this.input.clearHint(); - } - }, - _autocomplete: function autocomplete(laxCursor) { - var hint, query, isCursorAtEnd, datum; - hint = this.input.getHint(); - query = this.input.getQuery(); - isCursorAtEnd = laxCursor || this.input.isCursorAtEnd(); - if (hint && query !== hint && isCursorAtEnd) { - datum = this.dropdown.getDatumForTopSuggestion(); - datum && this.input.setInputValue(datum.value); - this.eventBus.trigger("autocompleted", datum.raw, datum.datasetName); - } - }, - _select: function select(datum) { - this.input.setQuery(datum.value); - this.input.setInputValue(datum.value, true); - this._setLanguageDirection(); - this.eventBus.trigger("selected", datum.raw, datum.datasetName); - this.dropdown.close(); - _.defer(_.bind(this.dropdown.empty, this.dropdown)); - }, - open: function open() { - this.dropdown.open(); - }, - close: function close() { - this.dropdown.close(); - }, - setVal: function setVal(val) { - if (this.isActivated) { - this.input.setInputValue(val); - } else { - this.input.setQuery(val); - this.input.setInputValue(val, true); - } - this._setLanguageDirection(); - }, - getVal: function getVal() { - return this.input.getQuery(); - }, - destroy: function destroy() { - this.input.destroy(); - this.dropdown.destroy(); - destroyDomStructure(this.$node); - this.$node = null; - } - }); - return Typeahead; - function buildDomStructure(input, withHint) { - var $input, $wrapper, $dropdown, $hint; - $input = $(input); - $wrapper = $(html.wrapper).css(css.wrapper); - $dropdown = $(html.dropdown).css(css.dropdown); - $hint = $input.clone().css(css.hint).css(getBackgroundStyles($input)); - $hint.val("").removeData().addClass("tt-hint").removeAttr("id name placeholder").prop("disabled", true).attr({ - autocomplete: "off", - spellcheck: "false" - }); - $input.data(attrsKey, { - dir: $input.attr("dir"), - autocomplete: $input.attr("autocomplete"), - spellcheck: $input.attr("spellcheck"), - style: $input.attr("style") + this.isActivated = false; + this.autoselect = !!o.autoselect; + this.minLength = _.isNumber(o.minLength) ? o.minLength : 1; + this.$node = buildDomStructure(o.input, o.withHint); + $menu = this.$node.find('.tt-dropdown-menu'); + $input = this.$node.find('.tt-input'); + $hint = this.$node.find('.tt-hint'); + $input.on('blur.tt', function($e) { + var active, isActive, hasActive; + active = document.activeElement; + isActive = $menu.is(active); + hasActive = $menu.has(active).length > 0; + if (_.isMsie() && (isActive || hasActive)) { + $e.preventDefault(); + $e.stopImmediatePropagation(); + _.defer(function() { + $input.focus(); }); - $input.addClass("tt-input").attr({ - autocomplete: "off", - spellcheck: false - }).css(withHint ? css.input : css.inputWithNoHint); - try { - !$input.attr("dir") && $input.attr("dir", "auto"); - } catch (e) {} - return $input.wrap($wrapper).parent().prepend(withHint ? $hint : null).append($dropdown); - } - function getBackgroundStyles($el) { - return { - backgroundAttachment: $el.css("background-attachment"), - backgroundClip: $el.css("background-clip"), - backgroundColor: $el.css("background-color"), - backgroundImage: $el.css("background-image"), - backgroundOrigin: $el.css("background-origin"), - backgroundPosition: $el.css("background-position"), - backgroundRepeat: $el.css("background-repeat"), - backgroundSize: $el.css("background-size") - }; - } - function destroyDomStructure($node) { - var $input = $node.find(".tt-input"); - _.each($input.data(attrsKey), function(val, key) { - _.isUndefined(val) ? $input.removeAttr(key) : $input.attr(key, val); - }); - $input.detach().removeData(attrsKey).removeClass("tt-input").insertAfter($node); - $node.remove(); + } + }); + $menu.on('mousedown.tt', function($e) { + $e.preventDefault(); + }); + this.eventBus = o.eventBus || new EventBus({ + el: $input + }); + this.dropdown = new Dropdown({ + menu: $menu, + datasets: o.datasets + }).onSync('suggestionClicked', this._onSuggestionClicked, this).onSync('cursorMoved', this._onCursorMoved, this).onSync('cursorRemoved', this._onCursorRemoved, this).onSync('opened', this._onOpened, this).onSync('closed', this._onClosed, this).onAsync('datasetRendered', this._onDatasetRendered, this); + this.input = new Input({ + input: $input, + hint: $hint + }).onSync('focused', this._onFocused, this).onSync('blurred', this._onBlurred, this).onSync('enterKeyed', this._onEnterKeyed, this).onSync('tabKeyed', this._onTabKeyed, this).onSync('escKeyed', this._onEscKeyed, this).onSync('upKeyed', this._onUpKeyed, this).onSync('downKeyed', this._onDownKeyed, this).onSync('leftKeyed', this._onLeftKeyed, this).onSync('rightKeyed', this._onRightKeyed, this).onSync('queryChanged', this._onQueryChanged, this).onSync('whitespaceChanged', this._onWhitespaceChanged, this); + this._setLanguageDirection(); + } + _.mixin(Typeahead.prototype, { + _onSuggestionClicked: function onSuggestionClicked(type, $el) { + var datum; + if (datum = this.dropdown.getDatumForSuggestion($el)) { + this._select(datum); + } + }, + _onCursorMoved: function onCursorMoved() { + var datum = this.dropdown.getDatumForCursor(); + this.input.setInputValue(datum.value, true); + this.eventBus.trigger('cursorchanged', datum.raw, datum.datasetName); + }, + _onCursorRemoved: function onCursorRemoved() { + this.input.resetInputValue(); + this._updateHint(); + }, + _onDatasetRendered: function onDatasetRendered() { + this._updateHint(); + }, + _onOpened: function onOpened() { + this._updateHint(); + this.eventBus.trigger('opened'); + }, + _onClosed: function onClosed() { + this.input.clearHint(); + this.eventBus.trigger('closed'); + }, + _onFocused: function onFocused() { + this.isActivated = true; + this.dropdown.open(); + }, + _onBlurred: function onBlurred() { + this.isActivated = false; + this.dropdown.empty(); + this.dropdown.close(); + this.setVal('', true); //LM + }, + _onEnterKeyed: function onEnterKeyed(type, $e) { + var cursorDatum, topSuggestionDatum; + cursorDatum = this.dropdown.getDatumForCursor(); + topSuggestionDatum = this.dropdown.getDatumForTopSuggestion(); + if (cursorDatum) { + this._select(cursorDatum); + $e.preventDefault(); + } else if (this.autoselect && topSuggestionDatum) { + this._select(topSuggestionDatum); + $e.preventDefault(); + } + }, + _onTabKeyed: function onTabKeyed(type, $e) { + var datum; + if (datum = this.dropdown.getDatumForCursor()) { + this._select(datum); + $e.preventDefault(); + } else { + this._autocomplete(true); + } + }, + _onEscKeyed: function onEscKeyed() { + this.dropdown.close(); + this.input.resetInputValue(); + }, + _onUpKeyed: function onUpKeyed() { + var query = this.input.getQuery(); + this.dropdown.isEmpty && query.length >= this.minLength ? this.dropdown.update(query) : this.dropdown.moveCursorUp(); + this.dropdown.open(); + }, + _onDownKeyed: function onDownKeyed() { + var query = this.input.getQuery(); + this.dropdown.isEmpty && query.length >= this.minLength ? this.dropdown.update(query) : this.dropdown.moveCursorDown(); + this.dropdown.open(); + }, + _onLeftKeyed: function onLeftKeyed() { + this.dir === 'rtl' && this._autocomplete(); + }, + _onRightKeyed: function onRightKeyed() { + this.dir === 'ltr' && this._autocomplete(); + }, + _onQueryChanged: function onQueryChanged(e, query) { + this.input.clearHintIfInvalid(); + query.length >= this.minLength ? this.dropdown.update(query) : this.dropdown.empty(); + this.dropdown.open(); + this._setLanguageDirection(); + }, + _onWhitespaceChanged: function onWhitespaceChanged() { + this._updateHint(); + this.dropdown.open(); + }, + _setLanguageDirection: function setLanguageDirection() { + var dir; + if (this.dir !== (dir = this.input.getLanguageDirection())) { + this.dir = dir; + this.$node.css('direction', dir); + this.dropdown.setLanguageDirection(dir); + } + }, + _updateHint: function updateHint() { + var datum, val, query, escapedQuery, frontMatchRegEx, match; + datum = this.dropdown.getDatumForTopSuggestion(); + if (datum && this.dropdown.isVisible() && !this.input.hasOverflow()) { + val = this.input.getInputValue(); + query = Input.normalizeQuery(val); + escapedQuery = _.escapeRegExChars(query); + frontMatchRegEx = new RegExp('^(?:' + escapedQuery + ')(.+$)', 'i'); + match = frontMatchRegEx.exec(datum.value); + match ? this.input.setHint(val + match[1]) : this.input.clearHint(); + } else { + this.input.clearHint(); + } + }, + _autocomplete: function autocomplete(laxCursor) { + var hint, query, isCursorAtEnd, datum; + hint = this.input.getHint(); + query = this.input.getQuery(); + isCursorAtEnd = laxCursor || this.input.isCursorAtEnd(); + if (hint && query !== hint && isCursorAtEnd) { + datum = this.dropdown.getDatumForTopSuggestion(); + datum && this.input.setInputValue(datum.value); + this.eventBus.trigger('autocompleted', datum.raw, datum.datasetName); + } + }, + _select: function select(datum) { + this.input.setQuery(datum.value); + this.input.setInputValue(datum.value, true); + this._setLanguageDirection(); + this.eventBus.trigger('selected', datum.raw, datum.datasetName); + this.dropdown.close(); + _.defer(_.bind(this.dropdown.empty, this.dropdown)); + }, + open: function open() { + this.dropdown.open(); + }, + close: function close() { + this.dropdown.close(); + }, + setVal: function setVal(val) { + if (this.isActivated) { + this.input.setInputValue(val); + } else { + this.input.setQuery(val); + this.input.setInputValue(val, true); + } + this._setLanguageDirection(); + }, + getVal: function getVal() { + return this.input.getQuery(); + }, + destroy: function destroy() { + this.input.destroy(); + this.dropdown.destroy(); + destroyDomStructure(this.$node); + this.$node = null; } + }); + return Typeahead; + function buildDomStructure(input, withHint) { + var $input, $wrapper, $dropdown, $hint; + $input = $(input); + $wrapper = $(html.wrapper).css(css.wrapper); + $dropdown = $(html.dropdown).css(css.dropdown); + $hint = $input.clone().css(css.hint).css(getBackgroundStyles($input)); + $hint.val('').removeData().addClass('tt-hint').removeAttr('id name placeholder').prop('disabled', true).attr({ + autocomplete: 'off', + spellcheck: 'false' + }); + $input.data(attrsKey, { + dir: $input.attr('dir'), + autocomplete: $input.attr('autocomplete'), + spellcheck: $input.attr('spellcheck'), + style: $input.attr('style') + }); + $input.addClass('tt-input').attr({ + autocomplete: 'off', + spellcheck: false + }).css(withHint ? css.input : css.inputWithNoHint); + try { + !$input.attr('dir') && $input.attr('dir', 'auto'); + } catch (e) {} + return $input.wrap($wrapper).parent().prepend(withHint ? $hint : null).append($dropdown); + } + function getBackgroundStyles($el) { + return { + backgroundAttachment: $el.css('background-attachment'), + backgroundClip: $el.css('background-clip'), + backgroundColor: $el.css('background-color'), + backgroundImage: $el.css('background-image'), + backgroundOrigin: $el.css('background-origin'), + backgroundPosition: $el.css('background-position'), + backgroundRepeat: $el.css('background-repeat'), + backgroundSize: $el.css('background-size') + }; + } + function destroyDomStructure($node) { + var $input = $node.find('.tt-input'); + _.each($input.data(attrsKey), function(val, key) { + _.isUndefined(val) ? $input.removeAttr(key) : $input.attr(key, val); + }); + $input.detach().removeData(attrsKey).removeClass('tt-input').insertAfter($node); + $node.remove(); + } }(); (function() { - var old, typeaheadKey, methods; - old = $.fn.typeahead; - typeaheadKey = "ttTypeahead"; - methods = { - initialize: function initialize(o, datasets) { - datasets = _.isArray(datasets) ? datasets : [].slice.call(arguments, 1); - o = o || {}; - return this.each(attach); - function attach() { - var $input = $(this), eventBus, typeahead; - _.each(datasets, function(d) { - d.highlight = !!o.highlight; - }); - typeahead = new Typeahead({ - input: $input, - eventBus: eventBus = new EventBus({ - el: $input - }), - withHint: _.isUndefined(o.hint) ? true : !!o.hint, - minLength: o.minLength, - autoselect: o.autoselect, - datasets: datasets - }); - $input.data(typeaheadKey, typeahead); - } - }, - open: function open() { - return this.each(openTypeahead); - function openTypeahead() { - var $input = $(this), typeahead; - if (typeahead = $input.data(typeaheadKey)) { - typeahead.open(); - } - } - }, - close: function close() { - return this.each(closeTypeahead); - function closeTypeahead() { - var $input = $(this), typeahead; - if (typeahead = $input.data(typeaheadKey)) { - typeahead.close(); - } - } - }, - val: function val(newVal) { - return !arguments.length ? getVal(this.first()) : this.each(setVal); - function setVal() { - var $input = $(this), typeahead; - if (typeahead = $input.data(typeaheadKey)) { - typeahead.setVal(newVal); - } - } - function getVal($input) { - var typeahead, query; - if (typeahead = $input.data(typeaheadKey)) { - query = typeahead.getVal(); - } - return query; - } - }, - destroy: function destroy() { - return this.each(unattach); - function unattach() { - var $input = $(this), typeahead; - if (typeahead = $input.data(typeaheadKey)) { - typeahead.destroy(); - $input.removeData(typeaheadKey); - } - } + var old, typeaheadKey, methods; + old = $.fn.typeahead; + typeaheadKey = 'ttTypeahead'; + methods = { + initialize: function initialize(o, datasets) { + datasets = _.isArray(datasets) ? datasets : [].slice.call(arguments, 1); + o = o || {}; + return this.each(attach); + function attach() { + var $input = $(this), eventBus, typeahead; + _.each(datasets, function(d) { + d.highlight = !!o.highlight; + }); + typeahead = new Typeahead({ + input: $input, + eventBus: eventBus = new EventBus({ + el: $input + }), + withHint: _.isUndefined(o.hint) ? true : !!o.hint, + minLength: o.minLength, + autoselect: o.autoselect, + datasets: datasets + }); + $input.data(typeaheadKey, typeahead); + } + }, + open: function open() { + return this.each(openTypeahead); + function openTypeahead() { + var $input = $(this), typeahead; + if (typeahead = $input.data(typeaheadKey)) { + typeahead.open(); } - }; - $.fn.typeahead = function(method) { - if (methods[method]) { - return methods[method].apply(this, [].slice.call(arguments, 1)); - } else { - return methods.initialize.apply(this, arguments); + } + }, + close: function close() { + return this.each(closeTypeahead); + function closeTypeahead() { + var $input = $(this), typeahead; + if (typeahead = $input.data(typeaheadKey)) { + typeahead.close(); } - }; - $.fn.typeahead.noConflict = function noConflict() { - $.fn.typeahead = old; - return this; - }; + } + }, + val: function val(newVal) { + return !arguments.length ? getVal(this.first()) : this.each(setVal); + function setVal() { + var $input = $(this), typeahead; + if (typeahead = $input.data(typeaheadKey)) { + typeahead.setVal(newVal); + } + } + function getVal($input) { + var typeahead, query; + if (typeahead = $input.data(typeaheadKey)) { + query = typeahead.getVal(); + } + return query; + } + }, + destroy: function destroy() { + return this.each(unattach); + function unattach() { + var $input = $(this), typeahead; + if (typeahead = $input.data(typeaheadKey)) { + typeahead.destroy(); + $input.removeData(typeaheadKey); + } + } + } + }; + $.fn.typeahead = function(method) { + if (methods[method]) { + return methods[method].apply(this, [].slice.call(arguments, 1)); + } else { + return methods.initialize.apply(this, arguments); + } + }; + $.fn.typeahead.noConflict = function noConflict() { + $.fn.typeahead = old; + return this; + }; })(); - - - -//})(window.jQuery); - - -}); -define('searchView',[ - 'App', - // Templates - 'text!tpl/search.html', - 'text!tpl/search_suggestion.html', - // Tools - 'typeahead' -], function(App, searchTpl, suggestionTpl) { - - var searchView = Backbone.View.extend({ - el: '#search', - /** + + + + //})(window.jQuery); + + + }); + define('searchView',[ + 'App', + // Templates + 'text!tpl/search.html', + 'text!tpl/search_suggestion.html', + // Tools + 'typeahead' + ], function(App, searchTpl, suggestionTpl) { + + var searchView = Backbone.View.extend({ + el: '#search', + /** * Init. */ - init: function() { - var tpl = _.template(searchTpl); - var className = 'form-control input-lg'; - var placeholder = 'Search reference'; - this.searchHtml = tpl({ - 'placeholder': placeholder, - 'className': className - }); - this.items = App.classes.concat(App.allItems); + init: function() { + var tpl = _.template(searchTpl); + var className = 'form-control input-lg'; + var placeholder = 'Search reference'; + this.searchHtml = tpl({ + 'placeholder': placeholder, + 'className': className + }); + this.items = App.classes.concat(App.allItems); - return this; - }, - /** + return this; + }, + /** * Render input field with Typehead activated. */ - render: function() { + render: function() { // Append the view to the dom - this.$el.append(this.searchHtml); + this.$el.append(this.searchHtml); - // Render Typeahead - var $searchInput = this.$el.find('input[type=text]'); - this.typeaheadRender($searchInput); - this.typeaheadEvents($searchInput); + // Render Typeahead + var $searchInput = this.$el.find('input[type=text]'); + this.typeaheadRender($searchInput); + this.typeaheadEvents($searchInput); - return this; - }, - /** + return this; + }, + /** * Apply Twitter Typeahead to the search input field. * @param {jquery} $input */ - typeaheadRender: function($input) { - var self = this; - $input.typeahead(null, { - 'displayKey': 'name', - 'minLength': 2, - //'highlight': true, - 'source': self.substringMatcher(this.items), - 'templates': { - 'empty': ' ', - 'suggestion': _.template(suggestionTpl) - } - }); - }, - /** + typeaheadRender: function($input) { + var self = this; + $input.typeahead(null, { + 'displayKey': 'name', + 'minLength': 2, + //'highlight': true, + 'source': self.substringMatcher(this.items), + 'templates': { + 'empty': ' ', + 'suggestion': _.template(suggestionTpl) + } + }); + }, + /** * Setup typeahead custom events (item selected). */ - typeaheadEvents: function($input) { - var self = this; - $input.on('typeahead:selected', function(e, item, datasetName) { - var selectedItem = self.items[item.idx]; - select(selectedItem); - }); - $input.on('keydown', function(e) { - if (e.which === 13) { // enter - var txt = $input.val(); - var f = _.find(self.items, function(it) { return it.name == txt; }); - if (f) { - select(f); + typeaheadEvents: function($input) { + var self = this; + $input.on('typeahead:selected', function(e, item, datasetName) { + var selectedItem = self.items[item.idx]; + select(selectedItem); + }); + $input.on('keydown', function(e) { + if (e.which === 13) { // enter + var txt = $input.val(); + var f = _.find(self.items, function(it) { return it.name == txt; }); + if (f) { + select(f); + } + } else if (e.which === 27) { + $input.blur(); } - } else if (e.which === 27) { - $input.blur(); - } - }); + }); - function select(selectedItem) { - var hash = App.router.getHash(selectedItem);// - App.router.navigate(hash, {'trigger': true}); - $('#item').focus(); - } - }, - /** + function select(selectedItem) { + var hash = App.router.getHash(selectedItem);// + App.router.navigate(hash, { 'trigger': true }); + $('#item').focus(); + } + }, + /** * substringMatcher function for Typehead (search for strings in an array). * @param {array} array * @returns {Function} */ - substringMatcher: function(array) { - return function findMatches(query, callback) { - var matches = [], substrRegex, arrayLength = array.length; - - // regex used to determine if a string contains the substring `query` - substrRegex = new RegExp(query, 'i'); - - // iterate through the pool of strings and for any string that - // contains the substring `query`, add it to the `matches` array - for (var i=0; i < arrayLength; i++) { - var item = array[i]; - if (substrRegex.test(item.name)) { + substringMatcher: function(array) { + return function findMatches(query, callback) { + var matches = [], substrRegex, arrayLength = array.length; + + // regex used to determine if a string contains the substring `query` + substrRegex = new RegExp(query, 'i'); + + // iterate through the pool of strings and for any string that + // contains the substring `query`, add it to the `matches` array + for (var i=0; i < arrayLength; i++) { + var item = array[i]; + if (substrRegex.test(item.name)) { // typeahead expects suggestions to be a js object - matches.push({ - 'itemtype': item.itemtype, - 'name': item.name, - 'className': item.class, - 'is_constructor': !!item.is_constructor, - 'final': item.final, - 'idx': i - }); + matches.push({ + 'itemtype': item.itemtype, + 'name': item.name, + 'className': item.class, + 'is_constructor': !!item.is_constructor, + 'final': item.final, + 'idx': i + }); + } } - } - callback(matches); - }; - } + callback(matches); + }; + } - }); + }); - return searchView; + return searchView; -}); + }); -define('text!tpl/list.html',[],function () { return '<% _.each(groups, function(group){ %>\n\n Deprecated: <%=item.name%><% if (item.isMethod) { %>()<% } %> is deprecated and will be removed in a future version of p5. <% if (item.deprecationMessage) { %><%=item.deprecationMessage%><% } %>\n
\n <% } %>\n \n\n <%= item.description %>\n\n <% if (item.extends) { %>\nExtends <%=item.extends%>
\n <% } %>\n\n <% if (item.module === \'p5.sound\') { %>\nThis function requires you include the p5.sound library. Add the following into the head of your index.html file:\n
<script src="path/to/p5.sound.js"></script>
\n \n <% } %>\n\n <% if (item.constRefs) { %>\n Used by:\n <%\n var refs = item.constRefs;\n for (var i = 0; i < refs.length; i ++) {\n var ref = refs[i];\n var name = ref;\n if (name.substr(0, 3) === \'p5.\') {\n name = name.substr(3);\n }\n if (i !== 0) {\n if (i == refs.length - 1) {\n %> and <%\n } else {\n %>, <%\n }\n }\n %><%= name %>()<%\n }\n %>\n
\n <% } %>\n\n <% syntaxes.forEach(function(syntax) { %>\n
<%= syntax %>
\n <% }) %>\n \n<%=item.return.type%>: <%= item.return.description %>
\n\n Deprecated: <%=item.name%><% if (item.isMethod) { %>()<% } %> is deprecated and will be removed in a future version of p5. <% if (item.deprecationMessage) { %><%=item.deprecationMessage%><% } %>\n
\n <% } %>\n \n\n <%= item.description %>\n\n <% if (item.extends) { %>\nExtends <%=item.extends%>
\n <% } %>\n\n <% if (item.module === \'p5.sound\') { %>\nThis function requires you include the p5.sound library. Add the following into the head of your index.html file:\n
<script src="path/to/p5.sound.js"></script>
\n \n <% } %>\n\n <% if (item.constRefs) { %>\n Used by:\n <%\n var refs = item.constRefs;\n for (var i = 0; i < refs.length; i ++) {\n var ref = refs[i];\n var name = ref;\n if (name.substr(0, 3) === \'p5.\') {\n name = name.substr(3);\n }\n if (i !== 0) {\n if (i == refs.length - 1) {\n %> and <%\n } else {\n %>, <%\n }\n }\n %><%= name %>()<%\n }\n %>\n
\n <% } %>\n\n <% syntaxes.forEach(function(syntax) { %>\n
<%= syntax %>
\n <% }) %>\n \n<%=item.return.type%>: <%= item.return.description %>
\n} and {@code } tags in the DOM with
* {@code class=prettyprint} and prettify them.
*
@@ -2539,135 +2539,135 @@ var prettyPrintOne;
* containing all the elements to pretty print.
* Defaults to {@code document.body}.
*/
-var prettyPrint;
-
-
-(function () {
- var win = window;
- // Keyword lists for various languages.
- // We use things that coerce to strings to make them compact when minified
- // and to defeat aggressive optimizers that fold large string constants.
- var FLOW_CONTROL_KEYWORDS = ["break,continue,do,else,for,if,return,while"];
- var C_KEYWORDS = [FLOW_CONTROL_KEYWORDS,"auto,case,char,const,default," +
- "double,enum,extern,float,goto,inline,int,long,register,short,signed," +
- "sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"];
- var COMMON_KEYWORDS = [C_KEYWORDS,"catch,class,delete,false,import," +
- "new,operator,private,protected,public,this,throw,true,try,typeof"];
- var CPP_KEYWORDS = [COMMON_KEYWORDS,"alignof,align_union,asm,axiom,bool," +
- "concept,concept_map,const_cast,constexpr,decltype,delegate," +
- "dynamic_cast,explicit,export,friend,generic,late_check," +
- "mutable,namespace,nullptr,property,reinterpret_cast,static_assert," +
- "static_cast,template,typeid,typename,using,virtual,where"];
- var JAVA_KEYWORDS = [COMMON_KEYWORDS,
- "abstract,assert,boolean,byte,extends,final,finally,implements,import," +
- "instanceof,interface,null,native,package,strictfp,super,synchronized," +
- "throws,transient"];
- var CSHARP_KEYWORDS = [JAVA_KEYWORDS,
- "as,base,by,checked,decimal,delegate,descending,dynamic,event," +
- "fixed,foreach,from,group,implicit,in,internal,into,is,let," +
- "lock,object,out,override,orderby,params,partial,readonly,ref,sbyte," +
- "sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort," +
- "var,virtual,where"];
- var COFFEE_KEYWORDS = "all,and,by,catch,class,else,extends,false,finally," +
- "for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then," +
- "throw,true,try,unless,until,when,while,yes";
- var JSCRIPT_KEYWORDS = [COMMON_KEYWORDS,
- "debugger,eval,export,function,get,null,set,undefined,var,with," +
- "Infinity,NaN"];
- var PERL_KEYWORDS = "caller,delete,die,do,dump,elsif,eval,exit,foreach,for," +
- "goto,if,import,last,local,my,next,no,our,print,package,redo,require," +
- "sub,undef,unless,until,use,wantarray,while,BEGIN,END";
- var PYTHON_KEYWORDS = [FLOW_CONTROL_KEYWORDS, "and,as,assert,class,def,del," +
- "elif,except,exec,finally,from,global,import,in,is,lambda," +
- "nonlocal,not,or,pass,print,raise,try,with,yield," +
- "False,True,None"];
- var RUBY_KEYWORDS = [FLOW_CONTROL_KEYWORDS, "alias,and,begin,case,class," +
- "def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo," +
- "rescue,retry,self,super,then,true,undef,unless,until,when,yield," +
- "BEGIN,END"];
- var RUST_KEYWORDS = [FLOW_CONTROL_KEYWORDS, "as,assert,const,copy,drop," +
- "enum,extern,fail,false,fn,impl,let,log,loop,match,mod,move,mut,priv," +
- "pub,pure,ref,self,static,struct,true,trait,type,unsafe,use"];
- var SH_KEYWORDS = [FLOW_CONTROL_KEYWORDS, "case,done,elif,esac,eval,fi," +
- "function,in,local,set,then,until"];
- var ALL_KEYWORDS = [
+ var prettyPrint;
+
+
+ (function () {
+ var win = window;
+ // Keyword lists for various languages.
+ // We use things that coerce to strings to make them compact when minified
+ // and to defeat aggressive optimizers that fold large string constants.
+ var FLOW_CONTROL_KEYWORDS = ['break,continue,do,else,for,if,return,while'];
+ var C_KEYWORDS = [FLOW_CONTROL_KEYWORDS,'auto,case,char,const,default,' +
+ 'double,enum,extern,float,goto,inline,int,long,register,short,signed,' +
+ 'sizeof,static,struct,switch,typedef,union,unsigned,void,volatile'];
+ var COMMON_KEYWORDS = [C_KEYWORDS,'catch,class,delete,false,import,' +
+ 'new,operator,private,protected,public,this,throw,true,try,typeof'];
+ var CPP_KEYWORDS = [COMMON_KEYWORDS,'alignof,align_union,asm,axiom,bool,' +
+ 'concept,concept_map,const_cast,constexpr,decltype,delegate,' +
+ 'dynamic_cast,explicit,export,friend,generic,late_check,' +
+ 'mutable,namespace,nullptr,property,reinterpret_cast,static_assert,' +
+ 'static_cast,template,typeid,typename,using,virtual,where'];
+ var JAVA_KEYWORDS = [COMMON_KEYWORDS,
+ 'abstract,assert,boolean,byte,extends,final,finally,implements,import,' +
+ 'instanceof,interface,null,native,package,strictfp,super,synchronized,' +
+ 'throws,transient'];
+ var CSHARP_KEYWORDS = [JAVA_KEYWORDS,
+ 'as,base,by,checked,decimal,delegate,descending,dynamic,event,' +
+ 'fixed,foreach,from,group,implicit,in,internal,into,is,let,' +
+ 'lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,' +
+ 'sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,' +
+ 'var,virtual,where'];
+ var COFFEE_KEYWORDS = 'all,and,by,catch,class,else,extends,false,finally,' +
+ 'for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,' +
+ 'throw,true,try,unless,until,when,while,yes';
+ var JSCRIPT_KEYWORDS = [COMMON_KEYWORDS,
+ 'debugger,eval,export,function,get,null,set,undefined,var,with,' +
+ 'Infinity,NaN'];
+ var PERL_KEYWORDS = 'caller,delete,die,do,dump,elsif,eval,exit,foreach,for,' +
+ 'goto,if,import,last,local,my,next,no,our,print,package,redo,require,' +
+ 'sub,undef,unless,until,use,wantarray,while,BEGIN,END';
+ var PYTHON_KEYWORDS = [FLOW_CONTROL_KEYWORDS, 'and,as,assert,class,def,del,' +
+ 'elif,except,exec,finally,from,global,import,in,is,lambda,' +
+ 'nonlocal,not,or,pass,print,raise,try,with,yield,' +
+ 'False,True,None'];
+ var RUBY_KEYWORDS = [FLOW_CONTROL_KEYWORDS, 'alias,and,begin,case,class,' +
+ 'def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,' +
+ 'rescue,retry,self,super,then,true,undef,unless,until,when,yield,' +
+ 'BEGIN,END'];
+ var RUST_KEYWORDS = [FLOW_CONTROL_KEYWORDS, 'as,assert,const,copy,drop,' +
+ 'enum,extern,fail,false,fn,impl,let,log,loop,match,mod,move,mut,priv,' +
+ 'pub,pure,ref,self,static,struct,true,trait,type,unsafe,use'];
+ var SH_KEYWORDS = [FLOW_CONTROL_KEYWORDS, 'case,done,elif,esac,eval,fi,' +
+ 'function,in,local,set,then,until'];
+ var ALL_KEYWORDS = [
CPP_KEYWORDS, CSHARP_KEYWORDS, JSCRIPT_KEYWORDS, PERL_KEYWORDS,
PYTHON_KEYWORDS, RUBY_KEYWORDS, SH_KEYWORDS];
- var C_TYPES = /^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)\b/;
+ var C_TYPES = /^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)\b/;
- // token style names. correspond to css classes
- /**
+ // token style names. correspond to css classes
+ /**
* token style for a string literal
* @const
*/
- var PR_STRING = 'str';
- /**
+ var PR_STRING = 'str';
+ /**
* token style for a keyword
* @const
*/
- var PR_KEYWORD = 'kwd';
- /**
+ var PR_KEYWORD = 'kwd';
+ /**
* token style for a comment
* @const
*/
- var PR_COMMENT = 'com';
- /**
+ var PR_COMMENT = 'com';
+ /**
* token style for a type
* @const
*/
- var PR_TYPE = 'typ';
- /**
+ var PR_TYPE = 'typ';
+ /**
* token style for a literal value. e.g. 1, null, true.
* @const
*/
- var PR_LITERAL = 'lit';
- /**
+ var PR_LITERAL = 'lit';
+ /**
* token style for a punctuation string.
* @const
*/
- var PR_PUNCTUATION = 'pun';
- /**
+ var PR_PUNCTUATION = 'pun';
+ /**
* token style for plain text.
* @const
*/
- var PR_PLAIN = 'pln';
+ var PR_PLAIN = 'pln';
- /**
+ /**
* token style for an sgml tag.
* @const
*/
- var PR_TAG = 'tag';
- /**
+ var PR_TAG = 'tag';
+ /**
* token style for a markup declaration such as a DOCTYPE.
* @const
*/
- var PR_DECLARATION = 'dec';
- /**
+ var PR_DECLARATION = 'dec';
+ /**
* token style for embedded source.
* @const
*/
- var PR_SOURCE = 'src';
- /**
+ var PR_SOURCE = 'src';
+ /**
* token style for an sgml attribute name.
* @const
*/
- var PR_ATTRIB_NAME = 'atn';
- /**
+ var PR_ATTRIB_NAME = 'atn';
+ /**
* token style for an sgml attribute value.
* @const
*/
- var PR_ATTRIB_VALUE = 'atv';
+ var PR_ATTRIB_VALUE = 'atv';
- /**
+ /**
* A class that indicates a section of markup that is not code, e.g. to allow
* embedding of line numbers within code listings.
* @const
*/
- var PR_NOCODE = 'nocode';
+ var PR_NOCODE = 'nocode';
-
-
- /**
+
+
+ /**
* A set of tokens that can precede a regular expression literal in
* javascript
* http://web.archive.org/web/20070717142515/http://www.mozilla.org/js/language/js20/rationale/syntax.html
@@ -2686,15 +2686,15 @@ var prettyPrint;
* @private
* @const
*/
- var REGEXP_PRECEDER_PATTERN = '(?:^^\\.?|[+-]|[!=]=?=?|\\#|%=?|&&?=?|\\(|\\*=?|[+\\-]=|->|\\/=?|::?|<=?|>>?>?=?|,|;|\\?|@|\\[|~|{|\\^\\^?=?|\\|\\|?=?|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\\s*';
-
- // CAVEAT: this does not properly handle the case where a regular
- // expression immediately follows another since a regular expression may
- // have flags for case-sensitivity and the like. Having regexp tokens
- // adjacent is not valid in any language I'm aware of, so I'm punting.
- // TODO: maybe style special characters inside a regexp as punctuation.
+ var REGEXP_PRECEDER_PATTERN = '(?:^^\\.?|[+-]|[!=]=?=?|\\#|%=?|&&?=?|\\(|\\*=?|[+\\-]=|->|\\/=?|::?|<=?|>>?>?=?|,|;|\\?|@|\\[|~|{|\\^\\^?=?|\\|\\|?=?|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\\s*';
- /**
+ // CAVEAT: this does not properly handle the case where a regular
+ // expression immediately follows another since a regular expression may
+ // have flags for case-sensitivity and the like. Having regexp tokens
+ // adjacent is not valid in any language I'm aware of, so I'm punting.
+ // TODO: maybe style special characters inside a regexp as punctuation.
+
+ /**
* Given a group of {@link RegExp}s, returns a {@code RegExp} that globally
* matches the union of the sets of strings matched by the input RegExp.
* Since it matches globally, if the input strings have a start-of-input
@@ -2702,138 +2702,138 @@ var prettyPrint;
* @param {Array.} regexs non multiline, non-global regexs.
* @return {RegExp} a global regex.
*/
- function combinePrefixPatterns(regexs) {
- var capturedGroupIndex = 0;
-
- var needToFoldCase = false;
- var ignoreCase = false;
- for (var i = 0, n = regexs.length; i < n; ++i) {
- var regex = regexs[i];
- if (regex.ignoreCase) {
- ignoreCase = true;
- } else if (/[a-z]/i.test(regex.source.replace(
- /\\u[0-9a-f]{4}|\\x[0-9a-f]{2}|\\[^ux]/gi, ''))) {
- needToFoldCase = true;
- ignoreCase = false;
- break;
- }
- }
-
- var escapeCharToCodeUnit = {
- 'b': 8,
- 't': 9,
- 'n': 0xa,
- 'v': 0xb,
- 'f': 0xc,
- 'r': 0xd
- };
-
- function decodeEscape(charsetPart) {
- var cc0 = charsetPart.charCodeAt(0);
- if (cc0 !== 92 /* \\ */) {
- return cc0;
+ function combinePrefixPatterns(regexs) {
+ var capturedGroupIndex = 0;
+
+ var needToFoldCase = false;
+ var ignoreCase = false;
+ for (var i = 0, n = regexs.length; i < n; ++i) {
+ var regex = regexs[i];
+ if (regex.ignoreCase) {
+ ignoreCase = true;
+ } else if (/[a-z]/i.test(regex.source.replace(
+ /\\u[0-9a-f]{4}|\\x[0-9a-f]{2}|\\[^ux]/gi, ''))) {
+ needToFoldCase = true;
+ ignoreCase = false;
+ break;
+ }
}
- var c1 = charsetPart.charAt(1);
- cc0 = escapeCharToCodeUnit[c1];
- if (cc0) {
- return cc0;
- } else if ('0' <= c1 && c1 <= '7') {
- return parseInt(charsetPart.substring(1), 8);
- } else if (c1 === 'u' || c1 === 'x') {
- return parseInt(charsetPart.substring(2), 16);
- } else {
- return charsetPart.charCodeAt(1);
+
+ var escapeCharToCodeUnit = {
+ 'b': 8,
+ 't': 9,
+ 'n': 0xa,
+ 'v': 0xb,
+ 'f': 0xc,
+ 'r': 0xd
+ };
+
+ function decodeEscape(charsetPart) {
+ var cc0 = charsetPart.charCodeAt(0);
+ if (cc0 !== 92 /* \\ */) {
+ return cc0;
+ }
+ var c1 = charsetPart.charAt(1);
+ cc0 = escapeCharToCodeUnit[c1];
+ if (cc0) {
+ return cc0;
+ } else if ('0' <= c1 && c1 <= '7') {
+ return parseInt(charsetPart.substring(1), 8);
+ } else if (c1 === 'u' || c1 === 'x') {
+ return parseInt(charsetPart.substring(2), 16);
+ } else {
+ return charsetPart.charCodeAt(1);
+ }
}
- }
-
- function encodeEscape(charCode) {
- if (charCode < 0x20) {
- return (charCode < 0x10 ? '\\x0' : '\\x') + charCode.toString(16);
+
+ function encodeEscape(charCode) {
+ if (charCode < 0x20) {
+ return (charCode < 0x10 ? '\\x0' : '\\x') + charCode.toString(16);
+ }
+ var ch = String.fromCharCode(charCode);
+ return (ch === '\\' || ch === '-' || ch === ']' || ch === '^')
+ ? '\\' + ch : ch;
}
- var ch = String.fromCharCode(charCode);
- return (ch === '\\' || ch === '-' || ch === ']' || ch === '^')
- ? "\\" + ch : ch;
- }
-
- function caseFoldCharset(charSet) {
- var charsetParts = charSet.substring(1, charSet.length - 1).match(
+
+ function caseFoldCharset(charSet) {
+ var charsetParts = charSet.substring(1, charSet.length - 1).match(
new RegExp(
- '\\\\u[0-9A-Fa-f]{4}'
+ '\\\\u[0-9A-Fa-f]{4}'
+ '|\\\\x[0-9A-Fa-f]{2}'
+ '|\\\\[0-3][0-7]{0,2}'
+ '|\\\\[0-7]{1,2}'
+ '|\\\\[\\s\\S]'
+ '|-'
+ '|[^-\\\\]',
- 'g'));
- var ranges = [];
- var inverse = charsetParts[0] === '^';
-
- var out = ['['];
- if (inverse) { out.push('^'); }
-
- for (var i = inverse ? 1 : 0, n = charsetParts.length; i < n; ++i) {
- var p = charsetParts[i];
- if (/\\[bdsw]/i.test(p)) { // Don't muck with named groups.
- out.push(p);
- } else {
- var start = decodeEscape(p);
- var end;
- if (i + 2 < n && '-' === charsetParts[i + 1]) {
- end = decodeEscape(charsetParts[i + 2]);
- i += 2;
+ 'g'));
+ var ranges = [];
+ var inverse = charsetParts[0] === '^';
+
+ var out = ['['];
+ if (inverse) { out.push('^'); }
+
+ for (var i = inverse ? 1 : 0, n = charsetParts.length; i < n; ++i) {
+ var p = charsetParts[i];
+ if (/\\[bdsw]/i.test(p)) { // Don't muck with named groups.
+ out.push(p);
} else {
- end = start;
- }
- ranges.push([start, end]);
- // If the range might intersect letters, then expand it.
- // This case handling is too simplistic.
- // It does not deal with non-latin case folding.
- // It works for latin source code identifiers though.
- if (!(end < 65 || start > 122)) {
- if (!(end < 65 || start > 90)) {
- ranges.push([Math.max(65, start) | 32, Math.min(end, 90) | 32]);
+ var start = decodeEscape(p);
+ var end;
+ if (i + 2 < n && '-' === charsetParts[i + 1]) {
+ end = decodeEscape(charsetParts[i + 2]);
+ i += 2;
+ } else {
+ end = start;
}
- if (!(end < 97 || start > 122)) {
- ranges.push([Math.max(97, start) & ~32, Math.min(end, 122) & ~32]);
+ ranges.push([start, end]);
+ // If the range might intersect letters, then expand it.
+ // This case handling is too simplistic.
+ // It does not deal with non-latin case folding.
+ // It works for latin source code identifiers though.
+ if (!(end < 65 || start > 122)) {
+ if (!(end < 65 || start > 90)) {
+ ranges.push([Math.max(65, start) | 32, Math.min(end, 90) | 32]);
+ }
+ if (!(end < 97 || start > 122)) {
+ ranges.push([Math.max(97, start) & ~32, Math.min(end, 122) & ~32]);
+ }
}
}
}
- }
-
- // [[1, 10], [3, 4], [8, 12], [14, 14], [16, 16], [17, 17]]
- // -> [[1, 12], [14, 14], [16, 17]]
- ranges.sort(function (a, b) { return (a[0] - b[0]) || (b[1] - a[1]); });
- var consolidatedRanges = [];
- var lastRange = [];
- for (var i = 0; i < ranges.length; ++i) {
- var range = ranges[i];
- if (range[0] <= lastRange[1] + 1) {
- lastRange[1] = Math.max(lastRange[1], range[1]);
- } else {
- consolidatedRanges.push(lastRange = range);
+
+ // [[1, 10], [3, 4], [8, 12], [14, 14], [16, 16], [17, 17]]
+ // -> [[1, 12], [14, 14], [16, 17]]
+ ranges.sort(function (a, b) { return (a[0] - b[0]) || (b[1] - a[1]); });
+ var consolidatedRanges = [];
+ var lastRange = [];
+ for (var i = 0; i < ranges.length; ++i) {
+ var range = ranges[i];
+ if (range[0] <= lastRange[1] + 1) {
+ lastRange[1] = Math.max(lastRange[1], range[1]);
+ } else {
+ consolidatedRanges.push(lastRange = range);
+ }
}
- }
-
- for (var i = 0; i < consolidatedRanges.length; ++i) {
- var range = consolidatedRanges[i];
- out.push(encodeEscape(range[0]));
- if (range[1] > range[0]) {
- if (range[1] + 1 > range[0]) { out.push('-'); }
- out.push(encodeEscape(range[1]));
+
+ for (var i = 0; i < consolidatedRanges.length; ++i) {
+ var range = consolidatedRanges[i];
+ out.push(encodeEscape(range[0]));
+ if (range[1] > range[0]) {
+ if (range[1] + 1 > range[0]) { out.push('-'); }
+ out.push(encodeEscape(range[1]));
+ }
}
+ out.push(']');
+ return out.join('');
}
- out.push(']');
- return out.join('');
- }
-
- function allowAnywhereFoldCaseAndRenumberGroups(regex) {
+
+ function allowAnywhereFoldCaseAndRenumberGroups(regex) {
// Split into character sets, escape sequences, punctuation strings
// like ('(', '(?:', ')', '^'), and runs of characters that do not
// include any of the above.
- var parts = regex.source.match(
+ var parts = regex.source.match(
new RegExp(
- '(?:'
+ '(?:'
+ '\\[(?:[^\\x5C\\x5D]|\\\\[\\s\\S])*\\]' // a character set
+ '|\\\\u[A-Fa-f0-9]{4}' // a unicode escape
+ '|\\\\x[A-Fa-f0-9]{2}' // a hex escape
@@ -2843,99 +2843,99 @@ var prettyPrint;
+ '|[\\(\\)\\^]' // start/end of a group, or line start
+ '|[^\\x5B\\x5C\\(\\)\\^]+' // run of other characters
+ ')',
- 'g'));
- var n = parts.length;
-
- // Maps captured group numbers to the number they will occupy in
- // the output or to -1 if that has not been determined, or to
- // undefined if they need not be capturing in the output.
- var capturedGroups = [];
-
- // Walk over and identify back references to build the capturedGroups
- // mapping.
- for (var i = 0, groupIndex = 0; i < n; ++i) {
- var p = parts[i];
- if (p === '(') {
+ 'g'));
+ var n = parts.length;
+
+ // Maps captured group numbers to the number they will occupy in
+ // the output or to -1 if that has not been determined, or to
+ // undefined if they need not be capturing in the output.
+ var capturedGroups = [];
+
+ // Walk over and identify back references to build the capturedGroups
+ // mapping.
+ for (var i = 0, groupIndex = 0; i < n; ++i) {
+ var p = parts[i];
+ if (p === '(') {
// groups are 1-indexed, so max group index is count of '('
- ++groupIndex;
- } else if ('\\' === p.charAt(0)) {
- var decimalValue = +p.substring(1);
- if (decimalValue) {
- if (decimalValue <= groupIndex) {
- capturedGroups[decimalValue] = -1;
- } else {
+ ++groupIndex;
+ } else if ('\\' === p.charAt(0)) {
+ var decimalValue = +p.substring(1);
+ if (decimalValue) {
+ if (decimalValue <= groupIndex) {
+ capturedGroups[decimalValue] = -1;
+ } else {
// Replace with an unambiguous escape sequence so that
// an octal escape sequence does not turn into a backreference
// to a capturing group from an earlier regex.
- parts[i] = encodeEscape(decimalValue);
+ parts[i] = encodeEscape(decimalValue);
+ }
}
}
}
- }
-
- // Renumber groups and reduce capturing groups to non-capturing groups
- // where possible.
- for (var i = 1; i < capturedGroups.length; ++i) {
- if (-1 === capturedGroups[i]) {
- capturedGroups[i] = ++capturedGroupIndex;
- }
- }
- for (var i = 0, groupIndex = 0; i < n; ++i) {
- var p = parts[i];
- if (p === '(') {
- ++groupIndex;
- if (!capturedGroups[groupIndex]) {
- parts[i] = '(?:';
+
+ // Renumber groups and reduce capturing groups to non-capturing groups
+ // where possible.
+ for (var i = 1; i < capturedGroups.length; ++i) {
+ if (-1 === capturedGroups[i]) {
+ capturedGroups[i] = ++capturedGroupIndex;
}
- } else if ('\\' === p.charAt(0)) {
- var decimalValue = +p.substring(1);
- if (decimalValue && decimalValue <= groupIndex) {
- parts[i] = '\\' + capturedGroups[decimalValue];
+ }
+ for (var i = 0, groupIndex = 0; i < n; ++i) {
+ var p = parts[i];
+ if (p === '(') {
+ ++groupIndex;
+ if (!capturedGroups[groupIndex]) {
+ parts[i] = '(?:';
+ }
+ } else if ('\\' === p.charAt(0)) {
+ var decimalValue = +p.substring(1);
+ if (decimalValue && decimalValue <= groupIndex) {
+ parts[i] = '\\' + capturedGroups[decimalValue];
+ }
}
}
- }
-
- // Remove any prefix anchors so that the output will match anywhere.
- // ^^ really does mean an anchored match though.
- for (var i = 0; i < n; ++i) {
- if ('^' === parts[i] && '^' !== parts[i + 1]) { parts[i] = ''; }
- }
-
- // Expand letters to groups to handle mixing of case-sensitive and
- // case-insensitive patterns if necessary.
- if (regex.ignoreCase && needToFoldCase) {
+
+ // Remove any prefix anchors so that the output will match anywhere.
+ // ^^ really does mean an anchored match though.
for (var i = 0; i < n; ++i) {
- var p = parts[i];
- var ch0 = p.charAt(0);
- if (p.length >= 2 && ch0 === '[') {
- parts[i] = caseFoldCharset(p);
- } else if (ch0 !== '\\') {
+ if ('^' === parts[i] && '^' !== parts[i + 1]) { parts[i] = ''; }
+ }
+
+ // Expand letters to groups to handle mixing of case-sensitive and
+ // case-insensitive patterns if necessary.
+ if (regex.ignoreCase && needToFoldCase) {
+ for (var i = 0; i < n; ++i) {
+ var p = parts[i];
+ var ch0 = p.charAt(0);
+ if (p.length >= 2 && ch0 === '[') {
+ parts[i] = caseFoldCharset(p);
+ } else if (ch0 !== '\\') {
// TODO: handle letters in numeric escapes.
- parts[i] = p.replace(
+ parts[i] = p.replace(
/[a-zA-Z]/g,
function (ch) {
var cc = ch.charCodeAt(0);
return '[' + String.fromCharCode(cc & ~32, cc | 32) + ']';
});
+ }
}
}
+
+ return parts.join('');
}
-
- return parts.join('');
- }
-
- var rewritten = [];
- for (var i = 0, n = regexs.length; i < n; ++i) {
- var regex = regexs[i];
- if (regex.global || regex.multiline) { throw new Error('' + regex); }
- rewritten.push(
+
+ var rewritten = [];
+ for (var i = 0, n = regexs.length; i < n; ++i) {
+ var regex = regexs[i];
+ if (regex.global || regex.multiline) { throw new Error('' + regex); }
+ rewritten.push(
'(?:' + allowAnywhereFoldCaseAndRenumberGroups(regex) + ')');
+ }
+
+ return new RegExp(rewritten.join('|'), ignoreCase ? 'gi' : 'g');
}
-
- return new RegExp(rewritten.join('|'), ignoreCase ? 'gi' : 'g');
- }
- /**
+ /**
* Split markup into a string of source code and an array mapping ranges in
* that string to the text nodes in which they appear.
*
@@ -2980,71 +2980,71 @@ var prettyPrint;
* be considered significant.
* @return {Object} source code and the text nodes in which they occur.
*/
- function extractSourceSpans(node, isPreformatted) {
- var nocode = /(?:^|\s)nocode(?:\s|$)/;
-
- var chunks = [];
- var length = 0;
- var spans = [];
- var k = 0;
-
- function walk(node) {
- var type = node.nodeType;
- if (type == 1) { // Element
- if (nocode.test(node.className)) { return; }
- for (var child = node.firstChild; child; child = child.nextSibling) {
- walk(child);
- }
- var nodeName = node.nodeName.toLowerCase();
- if ('br' === nodeName || 'li' === nodeName) {
- chunks[k] = '\n';
- spans[k << 1] = length++;
- spans[(k++ << 1) | 1] = node;
- }
- } else if (type == 3 || type == 4) { // Text
- var text = node.nodeValue;
- if (text.length) {
- if (!isPreformatted) {
- text = text.replace(/[ \t\r\n]+/g, ' ');
- } else {
- text = text.replace(/\r\n?/g, '\n'); // Normalize newlines.
+ function extractSourceSpans(node, isPreformatted) {
+ var nocode = /(?:^|\s)nocode(?:\s|$)/;
+
+ var chunks = [];
+ var length = 0;
+ var spans = [];
+ var k = 0;
+
+ function walk(node) {
+ var type = node.nodeType;
+ if (type == 1) { // Element
+ if (nocode.test(node.className)) { return; }
+ for (var child = node.firstChild; child; child = child.nextSibling) {
+ walk(child);
+ }
+ var nodeName = node.nodeName.toLowerCase();
+ if ('br' === nodeName || 'li' === nodeName) {
+ chunks[k] = '\n';
+ spans[k << 1] = length++;
+ spans[(k++ << 1) | 1] = node;
+ }
+ } else if (type == 3 || type == 4) { // Text
+ var text = node.nodeValue;
+ if (text.length) {
+ if (!isPreformatted) {
+ text = text.replace(/[ \t\r\n]+/g, ' ');
+ } else {
+ text = text.replace(/\r\n?/g, '\n'); // Normalize newlines.
+ }
+ // TODO: handle tabs here?
+ chunks[k] = text;
+ spans[k << 1] = length;
+ length += text.length;
+ spans[(k++ << 1) | 1] = node;
}
- // TODO: handle tabs here?
- chunks[k] = text;
- spans[k << 1] = length;
- length += text.length;
- spans[(k++ << 1) | 1] = node;
}
}
+
+ walk(node);
+
+ return {
+ sourceCode: chunks.join('').replace(/\n$/, ''),
+ spans: spans
+ };
}
-
- walk(node);
-
- return {
- sourceCode: chunks.join('').replace(/\n$/, ''),
- spans: spans
- };
- }
- /**
+ /**
* Apply the given language handler to sourceCode and add the resulting
* decorations to out.
* @param {number} basePos the index of sourceCode within the chunk of source
* whose decorations are already present on out.
*/
- function appendDecorations(basePos, sourceCode, langHandler, out) {
- if (!sourceCode) { return; }
- var job = {
- sourceCode: sourceCode,
- basePos: basePos
- };
- langHandler(job);
- out.push.apply(out, job.decorations);
- }
+ function appendDecorations(basePos, sourceCode, langHandler, out) {
+ if (!sourceCode) { return; }
+ var job = {
+ sourceCode: sourceCode,
+ basePos: basePos
+ };
+ langHandler(job);
+ out.push.apply(out, job.decorations);
+ }
- var notWs = /\S/;
+ var notWs = /\S/;
- /**
+ /**
* Given an element, if it contains only one child element and any text nodes
* it contains contain only space characters, return the sole child element.
* Otherwise returns undefined.
@@ -3055,20 +3055,20 @@ var prettyPrint;
* as in {@code ...
...
} or when there
* is textual content.
*/
- function childContentWrapper(element) {
- var wrapper = undefined;
- for (var c = element.firstChild; c; c = c.nextSibling) {
- var type = c.nodeType;
- wrapper = (type === 1) // Element Node
+ function childContentWrapper(element) {
+ var wrapper = undefined;
+ for (var c = element.firstChild; c; c = c.nextSibling) {
+ var type = c.nodeType;
+ wrapper = (type === 1) // Element Node
? (wrapper ? element : c)
: (type === 3) // Text Node
- ? (notWs.test(c.nodeValue) ? element : wrapper)
- : wrapper;
+ ? (notWs.test(c.nodeValue) ? element : wrapper)
+ : wrapper;
+ }
+ return wrapper === element ? undefined : wrapper;
}
- return wrapper === element ? undefined : wrapper;
- }
- /** Given triples of [style, pattern, context] returns a lexing function,
+ /** Given triples of [style, pattern, context] returns a lexing function,
* The lexing function interprets the patterns to find token boundaries and
* returns a decoration list of the form
* [index_0, style_0, index_1, style_1, ..., index_n, style_n]
@@ -3114,35 +3114,35 @@ var prettyPrint;
* @return {function (Object)} a
* function that takes source code and returns a list of decorations.
*/
- function createSimpleLexer(shortcutStylePatterns, fallthroughStylePatterns) {
- var shortcuts = {};
- var tokenizer;
- (function () {
- var allPatterns = shortcutStylePatterns.concat(fallthroughStylePatterns);
- var allRegexs = [];
- var regexKeys = {};
- for (var i = 0, n = allPatterns.length; i < n; ++i) {
- var patternParts = allPatterns[i];
- var shortcutChars = patternParts[3];
- if (shortcutChars) {
- for (var c = shortcutChars.length; --c >= 0;) {
- shortcuts[shortcutChars.charAt(c)] = patternParts;
+ function createSimpleLexer(shortcutStylePatterns, fallthroughStylePatterns) {
+ var shortcuts = {};
+ var tokenizer;
+ (function () {
+ var allPatterns = shortcutStylePatterns.concat(fallthroughStylePatterns);
+ var allRegexs = [];
+ var regexKeys = {};
+ for (var i = 0, n = allPatterns.length; i < n; ++i) {
+ var patternParts = allPatterns[i];
+ var shortcutChars = patternParts[3];
+ if (shortcutChars) {
+ for (var c = shortcutChars.length; --c >= 0;) {
+ shortcuts[shortcutChars.charAt(c)] = patternParts;
+ }
+ }
+ var regex = patternParts[1];
+ var k = '' + regex;
+ if (!regexKeys.hasOwnProperty(k)) {
+ allRegexs.push(regex);
+ regexKeys[k] = null;
}
}
- var regex = patternParts[1];
- var k = '' + regex;
- if (!regexKeys.hasOwnProperty(k)) {
- allRegexs.push(regex);
- regexKeys[k] = null;
- }
- }
- allRegexs.push(/[\0-\uffff]/);
- tokenizer = combinePrefixPatterns(allRegexs);
- })();
+ allRegexs.push(/[\0-\uffff]/);
+ tokenizer = combinePrefixPatterns(allRegexs);
+ })();
- var nPatterns = fallthroughStylePatterns.length;
+ var nPatterns = fallthroughStylePatterns.length;
- /**
+ /**
* Lexes job.sourceCode and produces an output array job.decorations of
* style classes preceded by the position at which they start in
* job.sourceCode in order.
@@ -3153,96 +3153,96 @@ var prettyPrint;
* sourceCode.
* }
*/
- var decorate = function (job) {
- var sourceCode = job.sourceCode, basePos = job.basePos;
- /** Even entries are positions in source in ascending order. Odd enties
+ var decorate = function (job) {
+ var sourceCode = job.sourceCode, basePos = job.basePos;
+ /** Even entries are positions in source in ascending order. Odd enties
* are style markers (e.g., PR_COMMENT) that run from that position until
* the end.
* @type {Array.{ @@ -3579,75 +3579,75 @@ var prettyPrint; * }* @private */ - function recombineTagsAndDecorations(job) { - var isIE8OrEarlier = /\bMSIE\s(\d+)/.exec(navigator.userAgent); - isIE8OrEarlier = isIE8OrEarlier && +isIE8OrEarlier[1] <= 8; - var newlineRe = /\n/g; - - var source = job.sourceCode; - var sourceLength = source.length; - // Index into source after the last code-unit recombined. - var sourceIndex = 0; - - var spans = job.spans; - var nSpans = spans.length; - // Index into spans after the last span which ends at or before sourceIndex. - var spanIndex = 0; - - var decorations = job.decorations; - var nDecorations = decorations.length; - // Index into decorations after the last decoration which ends at or before - // sourceIndex. - var decorationIndex = 0; - - // Remove all zero-length decorations. - decorations[nDecorations] = sourceLength; - var decPos, i; - for (i = decPos = 0; i < nDecorations;) { - if (decorations[i] !== decorations[i + 2]) { - decorations[decPos++] = decorations[i++]; - decorations[decPos++] = decorations[i++]; - } else { - i += 2; + function recombineTagsAndDecorations(job) { + var isIE8OrEarlier = /\bMSIE\s(\d+)/.exec(navigator.userAgent); + isIE8OrEarlier = isIE8OrEarlier && +isIE8OrEarlier[1] <= 8; + var newlineRe = /\n/g; + + var source = job.sourceCode; + var sourceLength = source.length; + // Index into source after the last code-unit recombined. + var sourceIndex = 0; + + var spans = job.spans; + var nSpans = spans.length; + // Index into spans after the last span which ends at or before sourceIndex. + var spanIndex = 0; + + var decorations = job.decorations; + var nDecorations = decorations.length; + // Index into decorations after the last decoration which ends at or before + // sourceIndex. + var decorationIndex = 0; + + // Remove all zero-length decorations. + decorations[nDecorations] = sourceLength; + var decPos, i; + for (i = decPos = 0; i < nDecorations;) { + if (decorations[i] !== decorations[i + 2]) { + decorations[decPos++] = decorations[i++]; + decorations[decPos++] = decorations[i++]; + } else { + i += 2; + } } - } - nDecorations = decPos; - - // Simplify decorations. - for (i = decPos = 0; i < nDecorations;) { - var startPos = decorations[i]; - // Conflate all adjacent decorations that use the same style. - var startDec = decorations[i + 1]; - var end = i + 2; - while (end + 2 <= nDecorations && decorations[end + 1] === startDec) { - end += 2; + nDecorations = decPos; + + // Simplify decorations. + for (i = decPos = 0; i < nDecorations;) { + var startPos = decorations[i]; + // Conflate all adjacent decorations that use the same style. + var startDec = decorations[i + 1]; + var end = i + 2; + while (end + 2 <= nDecorations && decorations[end + 1] === startDec) { + end += 2; + } + decorations[decPos++] = startPos; + decorations[decPos++] = startDec; + i = end; } - decorations[decPos++] = startPos; - decorations[decPos++] = startDec; - i = end; - } - - nDecorations = decorations.length = decPos; - - var sourceNode = job.sourceNode; - var oldDisplay; - if (sourceNode) { - oldDisplay = sourceNode.style.display; - sourceNode.style.display = 'none'; - } - try { - var decoration = null; - while (spanIndex < nSpans) { - var spanStart = spans[spanIndex]; - var spanEnd = spans[spanIndex + 2] || sourceLength; - - var decEnd = decorations[decorationIndex + 2] || sourceLength; - - var end = Math.min(spanEnd, decEnd); - - var textNode = spans[spanIndex + 1]; - var styledText; - if (textNode.nodeType !== 1 // Don't muck with