From cd7b48cd0da27f415194d3aaeb330ba6eb7d5d9d Mon Sep 17 00:00:00 2001 From: Elliot Smith Date: Tue, 12 Apr 2016 12:07:01 +0100 Subject: [PATCH] bitbake: toaster: migrate typeahead library Migrate from Bootstrap 2's built-in typeahead to Twitter's typeahead library. This is to facilitate moving to Bootstrap 3, which doesn't have a typeahead. (Bitbake rev: 0748177b40188a6fb735fe1ba1c17294afa4a3d0) Signed-off-by: Elliot Smith Signed-off-by: Michael Wood Signed-off-by: Richard Purdie --- bitbake/LICENSE | 2 + .../toaster/toastergui/static/css/default.css | 19 +++ .../toastergui/static/js/libtoaster.js | 122 ++++++++---------- .../static/js/typeahead.jquery.min.js | 7 + .../toaster/toastergui/templates/base.html | 2 + 5 files changed, 84 insertions(+), 68 deletions(-) create mode 100644 bitbake/lib/toaster/toastergui/static/js/typeahead.jquery.min.js diff --git a/bitbake/LICENSE b/bitbake/LICENSE index a57f9a419b..4ceabf7a73 100644 --- a/bitbake/LICENSE +++ b/bitbake/LICENSE @@ -9,4 +9,6 @@ Foundation and individual contributors. * jQuery is redistributed under the MIT license. +* Twitter typeahead.js redistributed under the MIT license. + * QUnit is redistributed under the MIT license. diff --git a/bitbake/lib/toaster/toastergui/static/css/default.css b/bitbake/lib/toaster/toastergui/static/css/default.css index b024d962aa..d0e45b6edc 100644 --- a/bitbake/lib/toaster/toastergui/static/css/default.css +++ b/bitbake/lib/toaster/toastergui/static/css/default.css @@ -341,3 +341,22 @@ input.input-lg { line-height: 1.33333; padding: 10px 16px; } + +/* styling for standalone typeahead library */ +.tt-menu { + margin-top: 2px; + border-radius: 4px; + width: 100%; +} + +.tt-suggestion { + cursor: pointer; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; +} + +.tt-suggestion.active { + background-color: #0081c2; + color: white; +} diff --git a/bitbake/lib/toaster/toastergui/static/js/libtoaster.js b/bitbake/lib/toaster/toastergui/static/js/libtoaster.js index 0d1486b831..d48c7f787a 100644 --- a/bitbake/lib/toaster/toastergui/static/js/libtoaster.js +++ b/bitbake/lib/toaster/toastergui/static/js/libtoaster.js @@ -3,96 +3,82 @@ * This object really just helps readability since we can then have * a traceable namespace. */ -var libtoaster = (function (){ +var libtoaster = (function () { + // prevent conflicts with Bootstrap 2's typeahead (required during + // transition from v2 to v3) + var typeahead = jQuery.fn.typeahead.noConflict(); + jQuery.fn._typeahead = typeahead; - /* makeTypeahead parameters - * elementSelector: JQuery elementSelector string - * xhrUrl: the url to get the JSON from expects JSON in the form: - * { "list": [ { "name": "test", "detail" : "a test thing" }, .... ] } + /* Make a typeahead from an input element + * + * _makeTypeahead parameters + * jQElement: input element as selected by $('selector') + * xhrUrl: the url to get the JSON from; this URL should return JSON in the + * format: + * { "results": [ { "name": "test", "detail" : "a test thing" }, ... ] } * xhrParams: the data/parameters to pass to the getJSON url e.g. - * { 'type' : 'projects' } the text typed will be passed as 'search'. - * selectedCB: function to call once an item has been selected one - * arg of the item. + * { 'type' : 'projects' }; the text typed will be passed as 'search'. + * selectedCB: function to call once an item has been selected; has + * signature selectedCB(item), where item is an item in the format shown + * in the JSON list above, i.e. + * { "name": "name", "detail": "detail" }. */ - function _makeTypeahead (jQElement, xhrUrl, xhrParams, selectedCB) { - if (!xhrUrl || xhrUrl.length === 0) - throw("No url to typeahead supplied"); + function _makeTypeahead(jQElement, xhrUrl, xhrParams, selectedCB) { + if (!xhrUrl || xhrUrl.length === 0) { + throw("No url supplied for typeahead"); + } var xhrReq; - jQElement.typeahead({ - // each time the typeahead's choices change, a - // "typeahead-choices-change" event is fired with an object - // containing the available choices in a "choices" property - source: function(query, process){ + jQElement._typeahead( + { + highlight: true, + classNames: { + open: "dropdown-menu", + cursor: "active" + } + }, + { + source: function (query, syncResults, asyncResults) { xhrParams.search = query; - /* If we have a request in progress don't fire off another one*/ - if (xhrReq) + // if we have a request in progress, cancel it and start another + if (xhrReq) { xhrReq.abort(); + } - xhrReq = $.getJSON(xhrUrl, this.options.xhrParams, function(data){ + xhrReq = $.getJSON(xhrUrl, xhrParams, function (data) { if (data.error !== "ok") { - console.log("Error getting data from server "+data.error); + console.error("Error getting data from server: " + data.error); return; } xhrReq = null; - jQElement.trigger("typeahead-choices-change", {choices: data.results}); - - return process(data.results); + asyncResults(data.results); }); }, - updater: function(item) { - var itemObj = this.$menu.find('.active').data('itemObject'); - selectedCB(itemObj); - return item; + + // how the selected item is shown in the input + display: function (item) { + return item.name; }, - matcher: function(item) { - if (!item.hasOwnProperty('name')) { - console.log("Name property missing in data"); - return 0; + + templates: { + // how the item is displayed in the dropdown + suggestion: function (item) { + var elt = document.createElement("div"); + elt.innerHTML = item.name + " " + item.detail; + return elt; } + } + } + ); - if (this.$element.val().length === 0) - return 0; - - return 1; - }, - highlighter: function (item) { - /* Use jquery to escape the item name and detail */ - var current = $("").text(item.name + ' '+item.detail); - current = current.html(); - - var query = this.query.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, '\\$&') - return current.replace(new RegExp('(' + query + ')', 'ig'), function ($1, match) { - return '' + match + '' - }) - }, - sorter: function (items) { return items; }, - xhrUrl: xhrUrl, - xhrParams: xhrParams, - xhrReq: xhrReq, + // when an item is selected using the typeahead, invoke the callback + jQElement.on("typeahead:select", function (event, item) { + selectedCB(item); }); - - - /* Copy of bootstrap's render func but sets selectedObject value */ - function customRenderFunc (items) { - var that = this; - - items = $(items).map(function (i, item) { - i = $(that.options.item).attr('data-value', item.name).data('itemObject', item); - i.find('a').html(that.highlighter(item)); - return i[0]; - }); - - items.first().addClass('active'); - this.$menu.html(items); - return this; - } - - jQElement.data('typeahead').render = customRenderFunc; } /* startABuild: diff --git a/bitbake/lib/toaster/toastergui/static/js/typeahead.jquery.min.js b/bitbake/lib/toaster/toastergui/static/js/typeahead.jquery.min.js new file mode 100644 index 0000000000..962133a40b --- /dev/null +++ b/bitbake/lib/toaster/toastergui/static/js/typeahead.jquery.min.js @@ -0,0 +1,7 @@ +/*! + * typeahead.js 0.11.1 + * https://github.com/twitter/typeahead.js + * Copyright 2013-2015 Twitter, Inc. and other contributors; Licensed MIT + */ + +!function(a,b){"function"==typeof define&&define.amd?define("typeahead.js",["jquery"],function(a){return b(a)}):"object"==typeof exports?module.exports=b(require("jquery")):b(jQuery)}(this,function(a){var b=function(){"use strict";return{isMsie:function(){return/(msie|trident)/i.test(navigator.userAgent)?navigator.userAgent.match(/(msie |rv:)(\d+(.\d+)?)/i)[2]:!1},isBlankString:function(a){return!a||/^\s*$/.test(a)},escapeRegExChars:function(a){return a.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,"\\$&")},isString:function(a){return"string"==typeof a},isNumber:function(a){return"number"==typeof a},isArray:a.isArray,isFunction:a.isFunction,isObject:a.isPlainObject,isUndefined:function(a){return"undefined"==typeof a},isElement:function(a){return!(!a||1!==a.nodeType)},isJQuery:function(b){return b instanceof a},toStr:function(a){return b.isUndefined(a)||null===a?"":a+""},bind:a.proxy,each:function(b,c){function d(a,b){return c(b,a)}a.each(b,d)},map:a.map,filter:a.grep,every:function(b,c){var d=!0;return b?(a.each(b,function(a,e){return(d=c.call(null,e,a,b))?void 0:!1}),!!d):d},some:function(b,c){var d=!1;return b?(a.each(b,function(a,e){return(d=c.call(null,e,a,b))?!1:void 0}),!!d):d},mixin:a.extend,identity:function(a){return a},clone:function(b){return a.extend(!0,{},b)},getIdGenerator:function(){var a=0;return function(){return a++}},templatify:function(b){function c(){return String(b)}return a.isFunction(b)?b:c},defer:function(a){setTimeout(a,0)},debounce:function(a,b,c){var d,e;return function(){var f,g,h=this,i=arguments;return f=function(){d=null,c||(e=a.apply(h,i))},g=c&&!d,clearTimeout(d),d=setTimeout(f,b),g&&(e=a.apply(h,i)),e}},throttle:function(a,b){var c,d,e,f,g,h;return g=0,h=function(){g=new Date,e=null,f=a.apply(c,d)},function(){var i=new Date,j=b-(i-g);return c=this,d=arguments,0>=j?(clearTimeout(e),e=null,g=i,f=a.apply(c,d)):e||(e=setTimeout(h,j)),f}},stringify:function(a){return b.isString(a)?a:JSON.stringify(a)},noop:function(){}}}(),c=function(){"use strict";function a(a){var g,h;return h=b.mixin({},f,a),g={css:e(),classes:h,html:c(h),selectors:d(h)},{css:g.css,html:g.html,classes:g.classes,selectors:g.selectors,mixin:function(a){b.mixin(a,g)}}}function c(a){return{wrapper:'',menu:'
'}}function d(a){var c={};return b.each(a,function(a,b){c[b]="."+a}),c}function e(){var a={wrapper:{position:"relative",display:"inline-block"},hint:{position:"absolute",top:"0",left:"0",borderColor:"transparent",boxShadow:"none",opacity:"1"},input:{position:"relative",verticalAlign:"top",backgroundColor:"transparent"},inputWithNoHint:{position:"relative",verticalAlign:"top"},menu:{position:"absolute",top:"100%",left:"0",zIndex:"100",display:"none"},ltr:{left:"0",right:"auto"},rtl:{left:"auto",right:" 0"}};return b.isMsie()&&b.mixin(a.input,{backgroundImage:"url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)"}),a}var f={wrapper:"twitter-typeahead",input:"tt-input",hint:"tt-hint",menu:"tt-menu",dataset:"tt-dataset",suggestion:"tt-suggestion",selectable:"tt-selectable",empty:"tt-empty",open:"tt-open",cursor:"tt-cursor",highlight:"tt-highlight"};return a}(),d=function(){"use strict";function c(b){b&&b.el||a.error("EventBus initialized without el"),this.$el=a(b.el)}var d,e;return d="typeahead:",e={render:"rendered",cursorchange:"cursorchanged",select:"selected",autocomplete:"autocompleted"},b.mixin(c.prototype,{_trigger:function(b,c){var e;return e=a.Event(d+b),(c=c||[]).unshift(e),this.$el.trigger.apply(this.$el,c),e},before:function(a){var b,c;return b=[].slice.call(arguments,1),c=this._trigger("before"+a,b),c.isDefaultPrevented()},trigger:function(a){var b;this._trigger(a,[].slice.call(arguments,1)),(b=e[a])&&this._trigger(b,[].slice.call(arguments,1))}}),c}(),e=function(){"use strict";function a(a,b,c,d){var e;if(!c)return this;for(b=b.split(i),c=d?h(c,d):c,this._callbacks=this._callbacks||{};e=b.shift();)this._callbacks[e]=this._callbacks[e]||{sync:[],async:[]},this._callbacks[e][a].push(c);return this}function b(b,c,d){return a.call(this,"async",b,c,d)}function c(b,c,d){return a.call(this,"sync",b,c,d)}function d(a){var b;if(!this._callbacks)return this;for(a=a.split(i);b=a.shift();)delete this._callbacks[b];return this}function e(a){var b,c,d,e,g;if(!this._callbacks)return this;for(a=a.split(i),d=[].slice.call(arguments,1);(b=a.shift())&&(c=this._callbacks[b]);)e=f(c.sync,this,[b].concat(d)),g=f(c.async,this,[b].concat(d)),e()&&j(g);return this}function f(a,b,c){function d(){for(var d,e=0,f=a.length;!d&&f>e;e+=1)d=a[e].apply(b,c)===!1;return!d}return d}function g(){var a;return a=window.setImmediate?function(a){setImmediate(function(){a()})}:function(a){setTimeout(function(){a()},0)}}function h(a,b){return a.bind?a.bind(b):function(){a.apply(b,[].slice.call(arguments,0))}}var i=/\s+/,j=g();return{onSync:c,onAsync:b,off:d,trigger:e}}(),f=function(a){"use strict";function c(a,c,d){for(var e,f=[],g=0,h=a.length;h>g;g++)f.push(b.escapeRegExChars(a[g]));return e=d?"\\b("+f.join("|")+")\\b":"("+f.join("|")+")",c?new RegExp(e):new RegExp(e,"i")}var d={node:null,pattern:null,tagName:"strong",className:null,wordsOnly:!1,caseSensitive:!1};return function(e){function f(b){var c,d,f;return(c=h.exec(b.data))&&(f=a.createElement(e.tagName),e.className&&(f.className=e.className),d=b.splitText(c.index),d.splitText(c[0].length),f.appendChild(d.cloneNode(!0)),b.parentNode.replaceChild(f,d)),!!c}function g(a,b){for(var c,d=3,e=0;e