summaryrefslogtreecommitdiffstats
path: root/modules-available
diff options
context:
space:
mode:
authorUdo Walter2016-12-08 14:33:08 +0100
committerUdo Walter2016-12-08 14:33:08 +0100
commit014251c9da06c0f1d7af49f4a45faf177c8c0530 (patch)
tree7542007ad1b37db740e9a4072199f794dbe8dc3b /modules-available
parent[statistics_reporting] added time cutoff ui (diff)
parent[roomplanner] Fix clipped tooltips, add border to PCs (diff)
downloadslx-admin-014251c9da06c0f1d7af49f4a45faf177c8c0530.tar.gz
slx-admin-014251c9da06c0f1d7af49f4a45faf177c8c0530.tar.xz
slx-admin-014251c9da06c0f1d7af49f4a45faf177c8c0530.zip
Merge branch 'master' of git.openslx.org:openslx-ng/slx-admin into statistics_reporting
Diffstat (limited to 'modules-available')
-rw-r--r--modules-available/exams/install.inc.php12
-rw-r--r--modules-available/js_selectize/clientscript.js6
-rw-r--r--modules-available/js_selectize/style.css3
-rw-r--r--modules-available/locations/inc/location.inc.php153
-rw-r--r--modules-available/locations/page.inc.php121
-rw-r--r--modules-available/locations/templates/location-subnets.html2
-rw-r--r--modules-available/locations/templates/locations.html5
-rw-r--r--modules-available/roomplanner/clientscript.js73
-rw-r--r--modules-available/roomplanner/inc/pvsgenerator.inc.php5
-rw-r--r--modules-available/roomplanner/js/grid.js6
-rw-r--r--modules-available/roomplanner/lang/de/messages.json1
-rw-r--r--modules-available/roomplanner/lang/en/messages.json1
-rw-r--r--modules-available/roomplanner/page.inc.php24
-rw-r--r--modules-available/roomplanner/style.css42
-rw-r--r--modules-available/roomplanner/templates/page.html2
-rw-r--r--modules-available/statistics/api.inc.php24
-rw-r--r--modules-available/statistics/inc/filter.inc.php8
-rw-r--r--modules-available/statistics/install.inc.php47
-rw-r--r--modules-available/statistics/page.inc.php72
-rw-r--r--modules-available/statistics/templates/cpumodels.html11
-rw-r--r--modules-available/statistics/templates/id44.html11
-rw-r--r--modules-available/statistics/templates/memory.html11
-rw-r--r--modules-available/statistics/templates/newclients.html18
-rw-r--r--modules-available/sysconfig/addmodule_adauth.inc.php10
-rw-r--r--modules-available/sysconfig/addmodule_ldapauth.inc.php7
-rw-r--r--modules-available/sysconfig/lang/de/messages.json1
-rw-r--r--modules-available/sysconfig/lang/de/template-tags.json4
-rw-r--r--modules-available/sysconfig/lang/en/messages.json1
-rw-r--r--modules-available/sysconfig/lang/en/module.json4
-rw-r--r--modules-available/sysconfig/lang/en/template-tags.json4
-rw-r--r--modules-available/sysconfig/templates/ad-selfsearch.html17
-rw-r--r--modules-available/sysconfig/templates/ad_ldap-checkconnection.html47
-rw-r--r--modules-available/sysconfig/templates/list-configs.html2
-rw-r--r--modules-available/sysconfig/templates/list-modules.html2
-rw-r--r--modules-available/webinterface/lang/en/template-tags.json4
35 files changed, 510 insertions, 251 deletions
diff --git a/modules-available/exams/install.inc.php b/modules-available/exams/install.inc.php
index 45e9e41a..1af5031d 100644
--- a/modules-available/exams/install.inc.php
+++ b/modules-available/exams/install.inc.php
@@ -27,10 +27,18 @@ if (Database::exec("ALTER TABLE exams ADD INDEX `idx_daterange` ( `starttime` ,
}
if (!tableHasColumn('exams', 'lectureid')) {
- Database::exec("ALTER TABLE `exams` ADD `lectureid` CHAR(36) CHARACTER SET ascii COLLATE ascii_bin NULL DEFAULT NULL AFTER `examid`");
+ $ret = Database::exec("ALTER TABLE `exams` ADD `lectureid` CHAR(36) CHARACTER SET ascii COLLATE ascii_bin NULL DEFAULT NULL AFTER `examid`");
+ if ($ret === false) {
+ finalResponse(UPDATE_FAILED, 'Adding lectureid to exams failed: ' . Database::lastError());
+ }
+ $res[] = UPDATE_DONE;
}
if (!tableHasColumn('exams', 'autologin')) {
- Database::exec("ALTER TABLE `exams` ADD `autologin` CHAR(36) NULL DEFAULT NULL AFTER `endtime`");
+ $ret = Database::exec("ALTER TABLE `exams` ADD `autologin` CHAR(36) NULL DEFAULT NULL AFTER `endtime`");
+ if ($ret === false) {
+ finalResponse(UPDATE_FAILED, 'Adding autologin to exams failed: ' . Database::lastError());
+ }
+ $res[] = UPDATE_DONE;
}
Database::exec("ALTER TABLE `exams` CHANGE `description` `description` varchar(500) DEFAULT NULL");
diff --git a/modules-available/js_selectize/clientscript.js b/modules-available/js_selectize/clientscript.js
index 305d531c..d11be61f 100644
--- a/modules-available/js_selectize/clientscript.js
+++ b/modules-available/js_selectize/clientscript.js
@@ -1,3 +1,3 @@
-/*! selectize.js - v0.12.2 | https://github.com/selectize/selectize.js | Apache License (v2) */
-!function(a,b){"function"==typeof define&&define.amd?define("sifter",b):"object"==typeof exports?module.exports=b():a.Sifter=b()}(this,function(){var a=function(a,b){this.items=a,this.settings=b||{diacritics:!0}};a.prototype.tokenize=function(a){if(a=e(String(a||"").toLowerCase()),!a||!a.length)return[];var b,c,d,g,i=[],j=a.split(/ +/);for(b=0,c=j.length;b<c;b++){if(d=f(j[b]),this.settings.diacritics)for(g in h)h.hasOwnProperty(g)&&(d=d.replace(new RegExp(g,"g"),h[g]));i.push({string:j[b],regex:new RegExp(d,"i")})}return i},a.prototype.iterator=function(a,b){var c;c=g(a)?Array.prototype.forEach||function(a){for(var b=0,c=this.length;b<c;b++)a(this[b],b,this)}:function(a){for(var b in this)this.hasOwnProperty(b)&&a(this[b],b,this)},c.apply(a,[b])},a.prototype.getScoreFunction=function(a,b){var c,e,f,g,h;c=this,a=c.prepareSearch(a,b),f=a.tokens,e=a.options.fields,g=f.length,h=a.options.nesting;var i=function(a,b){var c,d;return a?(a=String(a||""),d=a.search(b.regex),d===-1?0:(c=b.string.length/a.length,0===d&&(c+=.5),c)):0},j=function(){var a=e.length;return a?1===a?function(a,b){return i(d(b,e[0],h),a)}:function(b,c){for(var f=0,g=0;f<a;f++)g+=i(d(c,e[f],h),b);return g/a}:function(){return 0}}();return g?1===g?function(a){return j(f[0],a)}:"and"===a.options.conjunction?function(a){for(var b,c=0,d=0;c<g;c++){if(b=j(f[c],a),b<=0)return 0;d+=b}return d/g}:function(a){for(var b=0,c=0;b<g;b++)c+=j(f[b],a);return c/g}:function(){return 0}},a.prototype.getSortFunction=function(a,c){var e,f,g,h,i,j,k,l,m,n,o;if(g=this,a=g.prepareSearch(a,c),o=!a.query&&c.sort_empty||c.sort,m=function(a,b){return"$score"===a?b.score:d(g.items[b.id],a,c.nesting)},i=[],o)for(e=0,f=o.length;e<f;e++)(a.query||"$score"!==o[e].field)&&i.push(o[e]);if(a.query){for(n=!0,e=0,f=i.length;e<f;e++)if("$score"===i[e].field){n=!1;break}n&&i.unshift({field:"$score",direction:"desc"})}else for(e=0,f=i.length;e<f;e++)if("$score"===i[e].field){i.splice(e,1);break}for(l=[],e=0,f=i.length;e<f;e++)l.push("desc"===i[e].direction?-1:1);return j=i.length,j?1===j?(h=i[0].field,k=l[0],function(a,c){return k*b(m(h,a),m(h,c))}):function(a,c){var d,e,f;for(d=0;d<j;d++)if(f=i[d].field,e=l[d]*b(m(f,a),m(f,c)))return e;return 0}:null},a.prototype.prepareSearch=function(a,b){if("object"==typeof a)return a;b=c({},b);var d=b.fields,e=b.sort,f=b.sort_empty;return d&&!g(d)&&(b.fields=[d]),e&&!g(e)&&(b.sort=[e]),f&&!g(f)&&(b.sort_empty=[f]),{options:b,query:String(a||"").toLowerCase(),tokens:this.tokenize(a),total:0,items:[]}},a.prototype.search=function(a,b){var c,d,e,f,g=this;return d=this.prepareSearch(a,b),b=d.options,a=d.query,f=b.score||g.getScoreFunction(d),a.length?g.iterator(g.items,function(a,e){c=f(a),(b.filter===!1||c>0)&&d.items.push({score:c,id:e})}):g.iterator(g.items,function(a,b){d.items.push({score:1,id:b})}),e=g.getSortFunction(d,b),e&&d.items.sort(e),d.total=d.items.length,"number"==typeof b.limit&&(d.items=d.items.slice(0,b.limit)),d};var b=function(a,b){return"number"==typeof a&&"number"==typeof b?a>b?1:a<b?-1:0:(a=i(String(a||"")),b=i(String(b||"")),a>b?1:b>a?-1:0)},c=function(a,b){var c,d,e,f;for(c=1,d=arguments.length;c<d;c++)if(f=arguments[c])for(e in f)f.hasOwnProperty(e)&&(a[e]=f[e]);return a},d=function(a,b,c){if(a&&b){if(!c)return a[b];for(var d=b.split(".");d.length&&(a=a[d.shift()]););return a}},e=function(a){return(a+"").replace(/^\s+|\s+$|/g,"")},f=function(a){return(a+"").replace(/([.?*+^$[\]\\(){}|-])/g,"\\$1")},g=Array.isArray||"undefined"!=typeof $&&$.isArray||function(a){return"[object Array]"===Object.prototype.toString.call(a)},h={a:"[aḀḁĂăÂâǍǎȺⱥȦȧẠạÄäÀàÁáĀāÃãÅåąĄÃąĄ]",b:"[b␢βΒB฿𐌁ᛒ]",c:"[cĆćĈĉČčĊċC̄c̄ÇçḈḉȻȼƇƈɕᴄCc]",d:"[dĎďḊḋḐḑḌḍḒḓḎḏĐđD̦d̦ƉɖƊɗƋƌᵭᶁᶑȡᴅDdð]",e:"[eÉéÈèÊêḘḙĚěĔĕẼẽḚḛẺẻĖėËëĒēȨȩĘęᶒɆɇȄȅẾếỀềỄễỂểḜḝḖḗḔḕȆȇẸẹỆệⱸᴇEeɘǝƏƐε]",f:"[fƑƒḞḟ]",g:"[gɢ₲ǤǥĜĝĞğĢģƓɠĠġ]",h:"[hĤĥĦħḨḩẖẖḤḥḢḣɦʰǶƕ]",i:"[iÍíÌìĬĭÎîǏǐÏïḮḯĨĩĮįĪīỈỉȈȉȊȋỊịḬḭƗɨɨ̆ᵻᶖİiIıɪIi]",j:"[jȷĴĵɈɉʝɟʲ]",k:"[kƘƙꝀꝁḰḱǨǩḲḳḴḵκϰ₭]",l:"[lŁłĽľĻļĹĺḶḷḸḹḼḽḺḻĿŀȽƚⱠⱡⱢɫɬᶅɭȴʟLl]",n:"[nŃńǸǹŇňÑñṄṅŅņṆṇṊṋṈṉN̈n̈ƝɲȠƞᵰᶇɳȵɴNnŊŋ]",o:"[oØøÖöÓóÒòÔôǑǒŐőŎŏȮȯỌọƟɵƠơỎỏŌōÕõǪǫȌȍՕօ]",p:"[pṔṕṖṗⱣᵽƤƥᵱ]",q:"[qꝖꝗʠɊɋꝘꝙq̃]",r:"[rŔŕɌɍŘřŖŗṘṙȐȑȒȓṚṛⱤɽ]",s:"[sŚśṠṡṢṣꞨꞩŜŝŠšŞşȘșS̈s̈]",t:"[tŤťṪṫŢţṬṭƮʈȚțṰṱṮṯƬƭ]",u:"[uŬŭɄʉỤụÜüÚúÙùÛûǓǔŰűŬŭƯưỦủŪūŨũŲųȔȕ∪]",v:"[vṼṽṾṿƲʋꝞꝟⱱʋ]",w:"[wẂẃẀẁŴŵẄẅẆẇẈẉ]",x:"[xẌẍẊẋχ]",y:"[yÝýỲỳŶŷŸÿỸỹẎẏỴỵɎɏƳƴ]",z:"[zŹźẐẑŽžŻżẒẓẔẕƵƶ]"},i=function(){var a,b,c,d,e="",f={};for(c in h)if(h.hasOwnProperty(c))for(d=h[c].substring(2,h[c].length-1),e+=d,a=0,b=d.length;a<b;a++)f[d.charAt(a)]=c;var g=new RegExp("["+e+"]","g");return function(a){return a.replace(g,function(a){return f[a]}).toLowerCase()}}();return a}),function(a,b){"function"==typeof define&&define.amd?define("microplugin",b):"object"==typeof exports?module.exports=b():a.MicroPlugin=b()}(this,function(){var a={};a.mixin=function(a){a.plugins={},a.prototype.initializePlugins=function(a){var c,d,e,f=this,g=[];if(f.plugins={names:[],settings:{},requested:{},loaded:{}},b.isArray(a))for(c=0,d=a.length;c<d;c++)"string"==typeof a[c]?g.push(a[c]):(f.plugins.settings[a[c].name]=a[c].options,g.push(a[c].name));else if(a)for(e in a)a.hasOwnProperty(e)&&(f.plugins.settings[e]=a[e],g.push(e));for(;g.length;)f.require(g.shift())},a.prototype.loadPlugin=function(b){var c=this,d=c.plugins,e=a.plugins[b];if(!a.plugins.hasOwnProperty(b))throw new Error('Unable to find "'+b+'" plugin');d.requested[b]=!0,d.loaded[b]=e.fn.apply(c,[c.plugins.settings[b]||{}]),d.names.push(b)},a.prototype.require=function(a){var b=this,c=b.plugins;if(!b.plugins.loaded.hasOwnProperty(a)){if(c.requested[a])throw new Error('Plugin has circular dependency ("'+a+'")');b.loadPlugin(a)}return c.loaded[a]},a.define=function(b,c){a.plugins[b]={name:b,fn:c}}};var b={isArray:Array.isArray||function(a){return"[object Array]"===Object.prototype.toString.call(a)}};return a}),function(a,b){"function"==typeof define&&define.amd?define("selectize",["jquery","sifter","microplugin"],b):"object"==typeof exports?module.exports=b(require("jquery"),require("sifter"),require("microplugin")):a.Selectize=b(a.jQuery,a.Sifter,a.MicroPlugin)}(this,function(a,b,c){"use strict";var d=function(a,b){if("string"!=typeof b||b.length){var c="string"==typeof b?new RegExp(b,"i"):b,d=function(a){var b=0;if(3===a.nodeType){var e=a.data.search(c);if(e>=0&&a.data.length>0){var f=a.data.match(c),g=document.createElement("span");g.className="highlight";var h=a.splitText(e),i=(h.splitText(f[0].length),h.cloneNode(!0));g.appendChild(i),h.parentNode.replaceChild(g,h),b=1}}else if(1===a.nodeType&&a.childNodes&&!/(script|style)/i.test(a.tagName))for(var j=0;j<a.childNodes.length;++j)j+=d(a.childNodes[j]);return b};return a.each(function(){d(this)})}},e=function(){};e.prototype={on:function(a,b){this._events=this._events||{},this._events[a]=this._events[a]||[],this._events[a].push(b)},off:function(a,b){var c=arguments.length;return 0===c?delete this._events:1===c?delete this._events[a]:(this._events=this._events||{},void(a in this._events!=!1&&this._events[a].splice(this._events[a].indexOf(b),1)))},trigger:function(a){if(this._events=this._events||{},a in this._events!=!1)for(var b=0;b<this._events[a].length;b++)this._events[a][b].apply(this,Array.prototype.slice.call(arguments,1))}},e.mixin=function(a){for(var b=["on","off","trigger"],c=0;c<b.length;c++)a.prototype[b[c]]=e.prototype[b[c]]};var f=/Mac/.test(navigator.userAgent),g=65,h=13,i=27,j=37,k=38,l=80,m=39,n=40,o=78,p=8,q=46,r=16,s=f?91:17,t=f?18:17,u=9,v=1,w=2,x=!/android/i.test(window.navigator.userAgent)&&!!document.createElement("form").validity,y=function(a){return"undefined"!=typeof a},z=function(a){return"undefined"==typeof a||null===a?null:"boolean"==typeof a?a?"1":"0":a+""},A=function(a){return(a+"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},B={};B.before=function(a,b,c){var d=a[b];a[b]=function(){return c.apply(a,arguments),d.apply(a,arguments)}},B.after=function(a,b,c){var d=a[b];a[b]=function(){var b=d.apply(a,arguments);return c.apply(a,arguments),b}};var C=function(a){var b=!1;return function(){b||(b=!0,a.apply(this,arguments))}},D=function(a,b){var c;return function(){var d=this,e=arguments;window.clearTimeout(c),c=window.setTimeout(function(){a.apply(d,e)},b)}},E=function(a,b,c){var d,e=a.trigger,f={};a.trigger=function(){var c=arguments[0];return b.indexOf(c)===-1?e.apply(a,arguments):void(f[c]=arguments)},c.apply(a,[]),a.trigger=e;for(d in f)f.hasOwnProperty(d)&&e.apply(a,f[d])},F=function(a,b,c,d){a.on(b,c,function(b){for(var c=b.target;c&&c.parentNode!==a[0];)c=c.parentNode;return b.currentTarget=c,d.apply(this,[b])})},G=function(a){var b={};if("selectionStart"in a)b.start=a.selectionStart,b.length=a.selectionEnd-b.start;else if(document.selection){a.focus();var c=document.selection.createRange(),d=document.selection.createRange().text.length;c.moveStart("character",-a.value.length),b.start=c.text.length-d,b.length=d}return b},H=function(a,b,c){var d,e,f={};if(c)for(d=0,e=c.length;d<e;d++)f[c[d]]=a.css(c[d]);else f=a.css();b.css(f)},I=function(b,c){if(!b)return 0;var d=a("<test>").css({position:"absolute",top:-99999,left:-99999,width:"auto",padding:0,whiteSpace:"pre"}).text(b).appendTo("body");H(c,d,["letterSpacing","fontSize","fontFamily","fontWeight","textTransform"]);var e=d.width();return d.remove(),e},J=function(a){var b=null,c=function(c,d){var e,f,g,h,i,j,k,l;c=c||window.event||{},d=d||{},c.metaKey||c.altKey||(d.force||a.data("grow")!==!1)&&(e=a.val(),c.type&&"keydown"===c.type.toLowerCase()&&(f=c.keyCode,g=f>=97&&f<=122||f>=65&&f<=90||f>=48&&f<=57||32===f,f===q||f===p?(l=G(a[0]),l.length?e=e.substring(0,l.start)+e.substring(l.start+l.length):f===p&&l.start?e=e.substring(0,l.start-1)+e.substring(l.start+1):f===q&&"undefined"!=typeof l.start&&(e=e.substring(0,l.start)+e.substring(l.start+1))):g&&(j=c.shiftKey,k=String.fromCharCode(c.keyCode),k=j?k.toUpperCase():k.toLowerCase(),e+=k)),h=a.attr("placeholder"),!e&&h&&(e=h),i=I(e,a)+4,i!==b&&(b=i,a.width(i),a.triggerHandler("resize")))};a.on("keydown keyup update blur",c),c()},K=function(a){var b=document.createElement("div");return b.appendChild(a.cloneNode(!0)),b.innerHTML},L=function(c,d){var e,f,g,h,i=this;h=c[0],h.selectize=i;var j=window.getComputedStyle&&window.getComputedStyle(h,null);if(g=j?j.getPropertyValue("direction"):h.currentStyle&&h.currentStyle.direction,g=g||c.parents("[dir]:first").attr("dir")||"",a.extend(i,{order:0,settings:d,$input:c,tabIndex:c.attr("tabindex")||"",tagType:"select"===h.tagName.toLowerCase()?v:w,rtl:/rtl/i.test(g),eventNS:".selectize"+ ++L.count,highlightedValue:null,isOpen:!1,isDisabled:!1,isRequired:c.is("[required]"),isInvalid:!1,isLocked:!1,isFocused:!1,isInputHidden:!1,isSetup:!1,isShiftDown:!1,isCmdDown:!1,isCtrlDown:!1,ignoreFocus:!1,ignoreBlur:!1,ignoreHover:!1,hasOptions:!1,currentResults:null,lastValue:"",caretPos:0,loading:0,loadedSearches:{},$activeOption:null,$activeItems:[],optgroups:{},options:{},userOptions:{},items:[],renderCache:{},onSearchChange:null===d.loadThrottle?i.onSearchChange:D(i.onSearchChange,d.loadThrottle)}),i.sifter=new b(this.options,{diacritics:d.diacritics}),i.settings.options){for(e=0,f=i.settings.options.length;e<f;e++)i.registerOption(i.settings.options[e]);delete i.settings.options}if(i.settings.optgroups){for(e=0,f=i.settings.optgroups.length;e<f;e++)i.registerOptionGroup(i.settings.optgroups[e]);delete i.settings.optgroups}i.settings.mode=i.settings.mode||(1===i.settings.maxItems?"single":"multi"),"boolean"!=typeof i.settings.hideSelected&&(i.settings.hideSelected="multi"===i.settings.mode),i.initializePlugins(i.settings.plugins),i.setupCallbacks(),i.setupTemplates(),i.setup()};return e.mixin(L),c.mixin(L),a.extend(L.prototype,{setup:function(){var b,c,d,e,g,h,i,j,k,l=this,m=l.settings,n=l.eventNS,o=a(window),p=a(document),q=l.$input;if(i=l.settings.mode,j=q.attr("class")||"",b=a("<div>").addClass(m.wrapperClass).addClass(j).addClass(i),c=a("<div>").addClass(m.inputClass).addClass("items").appendTo(b),d=a('<input type="text" autocomplete="off" />').appendTo(c).attr("tabindex",q.is(":disabled")?"-1":l.tabIndex),h=a(m.dropdownParent||b),e=a("<div>").addClass(m.dropdownClass).addClass(i).hide().appendTo(h),g=a("<div>").addClass(m.dropdownContentClass).appendTo(e),l.settings.copyClassesToDropdown&&e.addClass(j),b.css({width:q[0].style.width}),l.plugins.names.length&&(k="plugin-"+l.plugins.names.join(" plugin-"),b.addClass(k),e.addClass(k)),(null===m.maxItems||m.maxItems>1)&&l.tagType===v&&q.attr("multiple","multiple"),l.settings.placeholder&&d.attr("placeholder",m.placeholder),!l.settings.splitOn&&l.settings.delimiter){var u=l.settings.delimiter.replace(/[-\/\\^$*+?.()|[\]{}]/g,"\\$&");l.settings.splitOn=new RegExp("\\s*"+u+"+\\s*")}q.attr("autocorrect")&&d.attr("autocorrect",q.attr("autocorrect")),q.attr("autocapitalize")&&d.attr("autocapitalize",q.attr("autocapitalize")),l.$wrapper=b,l.$control=c,l.$control_input=d,l.$dropdown=e,l.$dropdown_content=g,e.on("mouseenter","[data-selectable]",function(){return l.onOptionHover.apply(l,arguments)}),e.on("mousedown click","[data-selectable]",function(){return l.onOptionSelect.apply(l,arguments)}),F(c,"mousedown","*:not(input)",function(){return l.onItemSelect.apply(l,arguments)}),J(d),c.on({mousedown:function(){return l.onMouseDown.apply(l,arguments)},click:function(){return l.onClick.apply(l,arguments)}}),d.on({mousedown:function(a){a.stopPropagation()},keydown:function(){return l.onKeyDown.apply(l,arguments)},keyup:function(){return l.onKeyUp.apply(l,arguments)},keypress:function(){return l.onKeyPress.apply(l,arguments)},resize:function(){l.positionDropdown.apply(l,[])},blur:function(){return l.onBlur.apply(l,arguments)},focus:function(){return l.ignoreBlur=!1,l.onFocus.apply(l,arguments)},paste:function(){return l.onPaste.apply(l,arguments)}}),p.on("keydown"+n,function(a){l.isCmdDown=a[f?"metaKey":"ctrlKey"],l.isCtrlDown=a[f?"altKey":"ctrlKey"],l.isShiftDown=a.shiftKey}),p.on("keyup"+n,function(a){a.keyCode===t&&(l.isCtrlDown=!1),a.keyCode===r&&(l.isShiftDown=!1),a.keyCode===s&&(l.isCmdDown=!1)}),p.on("mousedown"+n,function(a){if(l.isFocused){if(a.target===l.$dropdown[0]||a.target.parentNode===l.$dropdown[0])return!1;l.$control.has(a.target).length||a.target===l.$control[0]||l.blur(a.target)}}),o.on(["scroll"+n,"resize"+n].join(" "),function(){l.isOpen&&l.positionDropdown.apply(l,arguments)}),o.on("mousemove"+n,function(){l.ignoreHover=!1}),this.revertSettings={$children:q.children().detach(),tabindex:q.attr("tabindex")},q.attr("tabindex",-1).hide().after(l.$wrapper),a.isArray(m.items)&&(l.setValue(m.items),delete m.items),x&&q.on("invalid"+n,function(a){a.preventDefault(),l.isInvalid=!0,l.refreshState()}),l.updateOriginalInput(),l.refreshItems(),l.refreshState(),l.updatePlaceholder(),l.isSetup=!0,q.is(":disabled")&&l.disable(),l.on("change",this.onChange),q.data("selectize",l),q.addClass("selectized"),l.trigger("initialize"),m.preload===!0&&l.onSearchChange("")},setupTemplates:function(){var b=this,c=b.settings.labelField,d=b.settings.optgroupLabelField,e={optgroup:function(a){return'<div class="optgroup">'+a.html+"</div>"},optgroup_header:function(a,b){return'<div class="optgroup-header">'+b(a[d])+"</div>"},option:function(a,b){return'<div class="option">'+b(a[c])+"</div>"},item:function(a,b){return'<div class="item">'+b(a[c])+"</div>"},option_create:function(a,b){return'<div class="create">Add <strong>'+b(a.input)+"</strong>&hellip;</div>"}};b.settings.render=a.extend({},e,b.settings.render)},setupCallbacks:function(){var a,b,c={initialize:"onInitialize",change:"onChange",item_add:"onItemAdd",item_remove:"onItemRemove",clear:"onClear",option_add:"onOptionAdd",option_remove:"onOptionRemove",option_clear:"onOptionClear",optgroup_add:"onOptionGroupAdd",optgroup_remove:"onOptionGroupRemove",optgroup_clear:"onOptionGroupClear",dropdown_open:"onDropdownOpen",dropdown_close:"onDropdownClose",type:"onType",load:"onLoad",focus:"onFocus",blur:"onBlur"};for(a in c)c.hasOwnProperty(a)&&(b=this.settings[c[a]],b&&this.on(a,b))},onClick:function(a){var b=this;b.isFocused||(b.focus(),a.preventDefault())},onMouseDown:function(b){var c=this,d=b.isDefaultPrevented();a(b.target);if(c.isFocused){if(b.target!==c.$control_input[0])return"single"===c.settings.mode?c.isOpen?c.close():c.open():d||c.setActiveItem(null),!1}else d||window.setTimeout(function(){c.focus()},0)},onChange:function(){this.$input.trigger("change")},onPaste:function(b){var c=this;c.isFull()||c.isInputHidden||c.isLocked?b.preventDefault():c.settings.splitOn&&setTimeout(function(){for(var b=a.trim(c.$control_input.val()||"").split(c.settings.splitOn),d=0,e=b.length;d<e;d++)c.createItem(b[d])},0)},onKeyPress:function(a){if(this.isLocked)return a&&a.preventDefault();var b=String.fromCharCode(a.keyCode||a.which);return this.settings.create&&"multi"===this.settings.mode&&b===this.settings.delimiter?(this.createItem(),a.preventDefault(),!1):void 0},onKeyDown:function(a){var b=(a.target===this.$control_input[0],this);if(b.isLocked)return void(a.keyCode!==u&&a.preventDefault());switch(a.keyCode){case g:if(b.isCmdDown)return void b.selectAll();break;case i:return void(b.isOpen&&(a.preventDefault(),a.stopPropagation(),b.close()));case o:if(!a.ctrlKey||a.altKey)break;case n:if(!b.isOpen&&b.hasOptions)b.open();else if(b.$activeOption){b.ignoreHover=!0;var c=b.getAdjacentOption(b.$activeOption,1);c.length&&b.setActiveOption(c,!0,!0)}return void a.preventDefault();case l:if(!a.ctrlKey||a.altKey)break;case k:if(b.$activeOption){b.ignoreHover=!0;var d=b.getAdjacentOption(b.$activeOption,-1);d.length&&b.setActiveOption(d,!0,!0)}return void a.preventDefault();case h:return void(b.isOpen&&b.$activeOption&&(b.onOptionSelect({currentTarget:b.$activeOption}),a.preventDefault()));case j:return void b.advanceSelection(-1,a);case m:return void b.advanceSelection(1,a);case u:return b.settings.selectOnTab&&b.isOpen&&b.$activeOption&&(b.onOptionSelect({currentTarget:b.$activeOption}),b.isFull()||a.preventDefault()),void(b.settings.create&&b.createItem()&&a.preventDefault());case p:case q:return void b.deleteSelection(a)}return!b.isFull()&&!b.isInputHidden||(f?a.metaKey:a.ctrlKey)?void 0:void a.preventDefault()},onKeyUp:function(a){var b=this;if(b.isLocked)return a&&a.preventDefault();var c=b.$control_input.val()||"";b.lastValue!==c&&(b.lastValue=c,b.onSearchChange(c),b.refreshOptions(),b.trigger("type",c))},onSearchChange:function(a){var b=this,c=b.settings.load;c&&(b.loadedSearches.hasOwnProperty(a)||(b.loadedSearches[a]=!0,b.load(function(d){c.apply(b,[a,d])})))},onFocus:function(a){var b=this,c=b.isFocused;return b.isDisabled?(b.blur(),a&&a.preventDefault(),!1):void(b.ignoreFocus||(b.isFocused=!0,"focus"===b.settings.preload&&b.onSearchChange(""),c||b.trigger("focus"),b.$activeItems.length||(b.showInput(),b.setActiveItem(null),b.refreshOptions(!!b.settings.openOnFocus)),b.refreshState()))},onBlur:function(a,b){var c=this;if(c.isFocused&&(c.isFocused=!1,!c.ignoreFocus)){if(!c.ignoreBlur&&document.activeElement===c.$dropdown_content[0])return c.ignoreBlur=!0,void c.onFocus(a);var d=function(){c.close(),c.setTextboxValue(""),c.setActiveItem(null),c.setActiveOption(null),c.setCaret(c.items.length),c.refreshState(),b&&b.focus(),c.ignoreFocus=!1,c.trigger("blur")};c.ignoreFocus=!0,c.settings.create&&c.settings.createOnBlur?c.createItem(null,!1,d):d()}},onOptionHover:function(a){this.ignoreHover||this.setActiveOption(a.currentTarget,!1)},onOptionSelect:function(b){var c,d,e=this;b.preventDefault&&(b.preventDefault(),b.stopPropagation()),d=a(b.currentTarget),d.hasClass("create")?e.createItem(null,function(){e.settings.closeAfterSelect&&e.close()}):(c=d.attr("data-value"),"undefined"!=typeof c&&(e.lastQuery=null,e.setTextboxValue(""),e.addItem(c),e.settings.closeAfterSelect?e.close():!e.settings.hideSelected&&b.type&&/mouse/.test(b.type)&&e.setActiveOption(e.getOption(c))))},onItemSelect:function(a){var b=this;b.isLocked||"multi"===b.settings.mode&&(a.preventDefault(),b.setActiveItem(a.currentTarget,a))},load:function(a){var b=this,c=b.$wrapper.addClass(b.settings.loadingClass);b.loading++,a.apply(b,[function(a){b.loading=Math.max(b.loading-1,0),a&&a.length&&(b.addOption(a),b.refreshOptions(b.isFocused&&!b.isInputHidden)),b.loading||c.removeClass(b.settings.loadingClass),b.trigger("load",a)}])},setTextboxValue:function(a){var b=this.$control_input,c=b.val()!==a;c&&(b.val(a).triggerHandler("update"),this.lastValue=a)},getValue:function(){return this.tagType===v&&this.$input.attr("multiple")?this.items:this.items.join(this.settings.delimiter)},setValue:function(a,b){var c=b?[]:["change"];E(this,c,function(){this.clear(b),this.addItems(a,b)})},setActiveItem:function(b,c){var d,e,f,g,h,i,j,k,l=this;if("single"!==l.settings.mode){if(b=a(b),!b.length)return a(l.$activeItems).removeClass("active"),l.$activeItems=[],void(l.isFocused&&l.showInput());if(d=c&&c.type.toLowerCase(),"mousedown"===d&&l.isShiftDown&&l.$activeItems.length){for(k=l.$control.children(".active:last"),g=Array.prototype.indexOf.apply(l.$control[0].childNodes,[k[0]]),h=Array.prototype.indexOf.apply(l.$control[0].childNodes,[b[0]]),g>h&&(j=g,g=h,h=j),e=g;e<=h;e++)i=l.$control[0].childNodes[e],l.$activeItems.indexOf(i)===-1&&(a(i).addClass("active"),l.$activeItems.push(i));c.preventDefault()}else"mousedown"===d&&l.isCtrlDown||"keydown"===d&&this.isShiftDown?b.hasClass("active")?(f=l.$activeItems.indexOf(b[0]),l.$activeItems.splice(f,1),b.removeClass("active")):l.$activeItems.push(b.addClass("active")[0]):(a(l.$activeItems).removeClass("active"),l.$activeItems=[b.addClass("active")[0]]);l.hideInput(),this.isFocused||l.focus()}},setActiveOption:function(b,c,d){var e,f,g,h,i,j=this;j.$activeOption&&j.$activeOption.removeClass("active"),j.$activeOption=null,b=a(b),b.length&&(j.$activeOption=b.addClass("active"),!c&&y(c)||(e=j.$dropdown_content.height(),f=j.$activeOption.outerHeight(!0),c=j.$dropdown_content.scrollTop()||0,g=j.$activeOption.offset().top-j.$dropdown_content.offset().top+c,h=g,i=g-e+f,g+f>e+c?j.$dropdown_content.stop().animate({scrollTop:i},d?j.settings.scrollDuration:0):g<c&&j.$dropdown_content.stop().animate({scrollTop:h},d?j.settings.scrollDuration:0)))},selectAll:function(){var a=this;"single"!==a.settings.mode&&(a.$activeItems=Array.prototype.slice.apply(a.$control.children(":not(input)").addClass("active")),a.$activeItems.length&&(a.hideInput(),a.close()),a.focus())},hideInput:function(){var a=this;a.setTextboxValue(""),a.$control_input.css({opacity:0,position:"absolute",left:a.rtl?1e4:-1e4}),a.isInputHidden=!0},showInput:function(){this.$control_input.css({opacity:1,position:"relative",left:0}),this.isInputHidden=!1},focus:function(){var a=this;a.isDisabled||(a.ignoreFocus=!0,a.$control_input[0].focus(),window.setTimeout(function(){a.ignoreFocus=!1,a.onFocus()},0))},blur:function(a){this.$control_input[0].blur(),this.onBlur(null,a)},getScoreFunction:function(a){return this.sifter.getScoreFunction(a,this.getSearchOptions())},getSearchOptions:function(){var a=this.settings,b=a.sortField;return"string"==typeof b&&(b=[{field:b}]),{fields:a.searchField,conjunction:a.searchConjunction,sort:b}},search:function(b){var c,d,e,f=this,g=f.settings,h=this.getSearchOptions();if(g.score&&(e=f.settings.score.apply(this,[b]),"function"!=typeof e))throw new Error('Selectize "score" setting must be a function that returns a function');if(b!==f.lastQuery?(f.lastQuery=b,d=f.sifter.search(b,a.extend(h,{score:e})),f.currentResults=d):d=a.extend(!0,{},f.currentResults),g.hideSelected)for(c=d.items.length-1;c>=0;c--)f.items.indexOf(z(d.items[c].id))!==-1&&d.items.splice(c,1);return d},refreshOptions:function(b){var c,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s;"undefined"==typeof b&&(b=!0);var t=this,u=a.trim(t.$control_input.val()),v=t.search(u),w=t.$dropdown_content,x=t.$activeOption&&z(t.$activeOption.attr("data-value"));for(g=v.items.length,"number"==typeof t.settings.maxOptions&&(g=Math.min(g,t.settings.maxOptions)),h={},i=[],c=0;c<g;c++)for(j=t.options[v.items[c].id],k=t.render("option",j),l=j[t.settings.optgroupField]||"",m=a.isArray(l)?l:[l],e=0,f=m&&m.length;e<f;e++)l=m[e],t.optgroups.hasOwnProperty(l)||(l=""),h.hasOwnProperty(l)||(h[l]=document.createDocumentFragment(),i.push(l)),h[l].appendChild(k);for(this.settings.lockOptgroupOrder&&i.sort(function(a,b){var c=t.optgroups[a].$order||0,d=t.optgroups[b].$order||0;return c-d}),n=document.createDocumentFragment(),c=0,g=i.length;c<g;c++)l=i[c],t.optgroups.hasOwnProperty(l)&&h[l].childNodes.length?(o=document.createDocumentFragment(),o.appendChild(t.render("optgroup_header",t.optgroups[l])),o.appendChild(h[l]),n.appendChild(t.render("optgroup",a.extend({},t.optgroups[l],{html:K(o),dom:o})))):n.appendChild(h[l]);if(w.html(n),t.settings.highlight&&v.query.length&&v.tokens.length)for(c=0,g=v.tokens.length;c<g;c++)d(w,v.tokens[c].regex);if(!t.settings.hideSelected)for(c=0,g=t.items.length;c<g;c++)t.getOption(t.items[c]).addClass("selected");p=t.canCreate(u),p&&(w.prepend(t.render("option_create",{input:u})),s=a(w[0].childNodes[0])),t.hasOptions=v.items.length>0||p,t.hasOptions?(v.items.length>0?(r=x&&t.getOption(x),r&&r.length?q=r:"single"===t.settings.mode&&t.items.length&&(q=t.getOption(t.items[0])),q&&q.length||(q=s&&!t.settings.addPrecedence?t.getAdjacentOption(s,1):w.find("[data-selectable]:first"))):q=s,t.setActiveOption(q),b&&!t.isOpen&&t.open()):(t.setActiveOption(null),b&&t.isOpen&&t.close())},addOption:function(b){var c,d,e,f=this;if(a.isArray(b))for(c=0,d=b.length;c<d;c++)f.addOption(b[c]);else(e=f.registerOption(b))&&(f.userOptions[e]=!0,f.lastQuery=null,f.trigger("option_add",e,b))},registerOption:function(a){var b=z(a[this.settings.valueField]);return"undefined"!=typeof b&&null!==b&&!this.options.hasOwnProperty(b)&&(a.$order=a.$order||++this.order,this.options[b]=a,b)},registerOptionGroup:function(a){var b=z(a[this.settings.optgroupValueField]);return!!b&&(a.$order=a.$order||++this.order,this.optgroups[b]=a,b)},addOptionGroup:function(a,b){b[this.settings.optgroupValueField]=a,(a=this.registerOptionGroup(b))&&this.trigger("optgroup_add",a,b)},removeOptionGroup:function(a){this.optgroups.hasOwnProperty(a)&&(delete this.optgroups[a],this.renderCache={},this.trigger("optgroup_remove",a))},clearOptionGroups:function(){this.optgroups={},this.renderCache={},this.trigger("optgroup_clear")},updateOption:function(b,c){var d,e,f,g,h,i,j,k=this;if(b=z(b),f=z(c[k.settings.valueField]),null!==b&&k.options.hasOwnProperty(b)){if("string"!=typeof f)throw new Error("Value must be set in option data");j=k.options[b].$order,f!==b&&(delete k.options[b],g=k.items.indexOf(b),g!==-1&&k.items.splice(g,1,f)),c.$order=c.$order||j,k.options[f]=c,h=k.renderCache.item,i=k.renderCache.option,h&&(delete h[b],delete h[f]),i&&(delete i[b],delete i[f]),k.items.indexOf(f)!==-1&&(d=k.getItem(b),e=a(k.render("item",c)),d.hasClass("active")&&e.addClass("active"),d.replaceWith(e)),k.lastQuery=null,k.isOpen&&k.refreshOptions(!1)}},removeOption:function(a,b){var c=this;a=z(a);var d=c.renderCache.item,e=c.renderCache.option;d&&delete d[a],e&&delete e[a],delete c.userOptions[a],delete c.options[a],c.lastQuery=null,c.trigger("option_remove",a),c.removeItem(a,b)},clearOptions:function(){var a=this;a.loadedSearches={},a.userOptions={},a.renderCache={},a.options=a.sifter.items={},a.lastQuery=null,a.trigger("option_clear"),a.clear()},getOption:function(a){return this.getElementWithValue(a,this.$dropdown_content.find("[data-selectable]"))},getAdjacentOption:function(b,c){var d=this.$dropdown.find("[data-selectable]"),e=d.index(b)+c;return e>=0&&e<d.length?d.eq(e):a()},getElementWithValue:function(b,c){if(b=z(b),"undefined"!=typeof b&&null!==b)for(var d=0,e=c.length;d<e;d++)if(c[d].getAttribute("data-value")===b)return a(c[d]);return a()},getItem:function(a){return this.getElementWithValue(a,this.$control.children())},addItems:function(b,c){for(var d=a.isArray(b)?b:[b],e=0,f=d.length;e<f;e++)this.isPending=e<f-1,this.addItem(d[e],c)},addItem:function(b,c){var d=c?[]:["change"];E(this,d,function(){var d,e,f,g,h,i=this,j=i.settings.mode;return b=z(b),i.items.indexOf(b)!==-1?void("single"===j&&i.close()):void(i.options.hasOwnProperty(b)&&("single"===j&&i.clear(c),"multi"===j&&i.isFull()||(d=a(i.render("item",i.options[b])),h=i.isFull(),i.items.splice(i.caretPos,0,b),i.insertAtCaret(d),(!i.isPending||!h&&i.isFull())&&i.refreshState(),i.isSetup&&(f=i.$dropdown_content.find("[data-selectable]"),i.isPending||(e=i.getOption(b),g=i.getAdjacentOption(e,1).attr("data-value"),i.refreshOptions(i.isFocused&&"single"!==j),g&&i.setActiveOption(i.getOption(g))),!f.length||i.isFull()?i.close():i.positionDropdown(),i.updatePlaceholder(),i.trigger("item_add",b,d),i.updateOriginalInput({silent:c})))))})},removeItem:function(b,c){var d,e,f,g=this;d=b instanceof a?b:g.getItem(b),b=z(d.attr("data-value")),e=g.items.indexOf(b),e!==-1&&(d.remove(),d.hasClass("active")&&(f=g.$activeItems.indexOf(d[0]),g.$activeItems.splice(f,1)),g.items.splice(e,1),g.lastQuery=null,!g.settings.persist&&g.userOptions.hasOwnProperty(b)&&g.removeOption(b,c),e<g.caretPos&&g.setCaret(g.caretPos-1),g.refreshState(),g.updatePlaceholder(),g.updateOriginalInput({silent:c}),g.positionDropdown(),g.trigger("item_remove",b,d))},createItem:function(b,c){var d=this,e=d.caretPos;b=b||a.trim(d.$control_input.val()||"");var f=arguments[arguments.length-1];if("function"!=typeof f&&(f=function(){}),"boolean"!=typeof c&&(c=!0),!d.canCreate(b))return f(),!1;d.lock();var g="function"==typeof d.settings.create?this.settings.create:function(a){var b={};return b[d.settings.labelField]=a,b[d.settings.valueField]=a,b},h=C(function(a){if(d.unlock(),!a||"object"!=typeof a)return f();var b=z(a[d.settings.valueField]);return"string"!=typeof b?f():(d.setTextboxValue(""),d.addOption(a),d.setCaret(e),d.addItem(b),d.refreshOptions(c&&"single"!==d.settings.mode),void f(a))}),i=g.apply(this,[b,h]);return"undefined"!=typeof i&&h(i),!0},refreshItems:function(){this.lastQuery=null,this.isSetup&&this.addItem(this.items),this.refreshState(),this.updateOriginalInput()},refreshState:function(){var a,b=this;b.isRequired&&(b.items.length&&(b.isInvalid=!1),b.$control_input.prop("required",a)),b.refreshClasses()},refreshClasses:function(){var b=this,c=b.isFull(),d=b.isLocked;b.$wrapper.toggleClass("rtl",b.rtl),b.$control.toggleClass("focus",b.isFocused).toggleClass("disabled",b.isDisabled).toggleClass("required",b.isRequired).toggleClass("invalid",b.isInvalid).toggleClass("locked",d).toggleClass("full",c).toggleClass("not-full",!c).toggleClass("input-active",b.isFocused&&!b.isInputHidden).toggleClass("dropdown-active",b.isOpen).toggleClass("has-options",!a.isEmptyObject(b.options)).toggleClass("has-items",b.items.length>0),b.$control_input.data("grow",!c&&!d)},isFull:function(){return null!==this.settings.maxItems&&this.items.length>=this.settings.maxItems},updateOriginalInput:function(a){var b,c,d,e,f=this;if(a=a||{},f.tagType===v){for(d=[],b=0,c=f.items.length;b<c;b++)e=f.options[f.items[b]][f.settings.labelField]||"",d.push('<option value="'+A(f.items[b])+'" selected="selected">'+A(e)+"</option>");d.length||this.$input.attr("multiple")||d.push('<option value="" selected="selected"></option>'),f.$input.html(d.join(""))}else f.$input.val(f.getValue()),f.$input.attr("value",f.$input.val());f.isSetup&&(a.silent||f.trigger("change",f.$input.val()))},updatePlaceholder:function(){if(this.settings.placeholder){var a=this.$control_input;this.items.length?a.removeAttr("placeholder"):a.attr("placeholder",this.settings.placeholder),a.triggerHandler("update",{force:!0})}},open:function(){var a=this;a.isLocked||a.isOpen||"multi"===a.settings.mode&&a.isFull()||(a.focus(),a.isOpen=!0,a.refreshState(),a.$dropdown.css({visibility:"hidden",display:"block"}),a.positionDropdown(),a.$dropdown.css({visibility:"visible"}),a.trigger("dropdown_open",a.$dropdown))},close:function(){var a=this,b=a.isOpen;"single"===a.settings.mode&&a.items.length&&a.hideInput(),a.isOpen=!1,a.$dropdown.hide(),a.setActiveOption(null),a.refreshState(),b&&a.trigger("dropdown_close",a.$dropdown)},positionDropdown:function(){
-var a=this.$control,b="body"===this.settings.dropdownParent?a.offset():a.position();b.top+=a.outerHeight(!0),this.$dropdown.css({width:a.outerWidth(),top:b.top,left:b.left})},clear:function(a){var b=this;b.items.length&&(b.$control.children(":not(input)").remove(),b.items=[],b.lastQuery=null,b.setCaret(0),b.setActiveItem(null),b.updatePlaceholder(),b.updateOriginalInput({silent:a}),b.refreshState(),b.showInput(),b.trigger("clear"))},insertAtCaret:function(b){var c=Math.min(this.caretPos,this.items.length);0===c?this.$control.prepend(b):a(this.$control[0].childNodes[c]).before(b),this.setCaret(c+1)},deleteSelection:function(b){var c,d,e,f,g,h,i,j,k,l=this;if(e=b&&b.keyCode===p?-1:1,f=G(l.$control_input[0]),l.$activeOption&&!l.settings.hideSelected&&(i=l.getAdjacentOption(l.$activeOption,-1).attr("data-value")),g=[],l.$activeItems.length){for(k=l.$control.children(".active:"+(e>0?"last":"first")),h=l.$control.children(":not(input)").index(k),e>0&&h++,c=0,d=l.$activeItems.length;c<d;c++)g.push(a(l.$activeItems[c]).attr("data-value"));b&&(b.preventDefault(),b.stopPropagation())}else(l.isFocused||"single"===l.settings.mode)&&l.items.length&&(e<0&&0===f.start&&0===f.length?g.push(l.items[l.caretPos-1]):e>0&&f.start===l.$control_input.val().length&&g.push(l.items[l.caretPos]));if(!g.length||"function"==typeof l.settings.onDelete&&l.settings.onDelete.apply(l,[g])===!1)return!1;for("undefined"!=typeof h&&l.setCaret(h);g.length;)l.removeItem(g.pop());return l.showInput(),l.positionDropdown(),l.refreshOptions(!0),i&&(j=l.getOption(i),j.length&&l.setActiveOption(j)),!0},advanceSelection:function(a,b){var c,d,e,f,g,h,i=this;0!==a&&(i.rtl&&(a*=-1),c=a>0?"last":"first",d=G(i.$control_input[0]),i.isFocused&&!i.isInputHidden?(f=i.$control_input.val().length,g=a<0?0===d.start&&0===d.length:d.start===f,g&&!f&&i.advanceCaret(a,b)):(h=i.$control.children(".active:"+c),h.length&&(e=i.$control.children(":not(input)").index(h),i.setActiveItem(null),i.setCaret(a>0?e+1:e))))},advanceCaret:function(a,b){var c,d,e=this;0!==a&&(c=a>0?"next":"prev",e.isShiftDown?(d=e.$control_input[c](),d.length&&(e.hideInput(),e.setActiveItem(d),b&&b.preventDefault())):e.setCaret(e.caretPos+a))},setCaret:function(b){var c=this;if(b="single"===c.settings.mode?c.items.length:Math.max(0,Math.min(c.items.length,b)),!c.isPending){var d,e,f,g;for(f=c.$control.children(":not(input)"),d=0,e=f.length;d<e;d++)g=a(f[d]).detach(),d<b?c.$control_input.before(g):c.$control.append(g)}c.caretPos=b},lock:function(){this.close(),this.isLocked=!0,this.refreshState()},unlock:function(){this.isLocked=!1,this.refreshState()},disable:function(){var a=this;a.$input.prop("disabled",!0),a.$control_input.prop("disabled",!0).prop("tabindex",-1),a.isDisabled=!0,a.lock()},enable:function(){var a=this;a.$input.prop("disabled",!1),a.$control_input.prop("disabled",!1).prop("tabindex",a.tabIndex),a.isDisabled=!1,a.unlock()},destroy:function(){var b=this,c=b.eventNS,d=b.revertSettings;b.trigger("destroy"),b.off(),b.$wrapper.remove(),b.$dropdown.remove(),b.$input.html("").append(d.$children).removeAttr("tabindex").removeClass("selectized").attr({tabindex:d.tabindex}).show(),b.$control_input.removeData("grow"),b.$input.removeData("selectize"),a(window).off(c),a(document).off(c),a(document.body).off(c),delete b.$input[0].selectize},render:function(b,c){var d,e,f="",g=!1,h=this;return"option"!==b&&"item"!==b||(d=z(c[h.settings.valueField]),g=!!d),g&&(y(h.renderCache[b])||(h.renderCache[b]={}),h.renderCache[b].hasOwnProperty(d))?h.renderCache[b][d]:(f=a(h.settings.render[b].apply(this,[c,A])),"option"===b||"option_create"===b?f.attr("data-selectable",""):"optgroup"===b&&(e=c[h.settings.optgroupValueField]||"",f.attr("data-group",e)),"option"!==b&&"item"!==b||f.attr("data-value",d||""),g&&(h.renderCache[b][d]=f[0]),f[0])},clearCache:function(a){var b=this;"undefined"==typeof a?b.renderCache={}:delete b.renderCache[a]},canCreate:function(a){var b=this;if(!b.settings.create)return!1;var c=b.settings.createFilter;return a.length&&("function"!=typeof c||c.apply(b,[a]))&&("string"!=typeof c||new RegExp(c).test(a))&&(!(c instanceof RegExp)||c.test(a))}}),L.count=0,L.defaults={options:[],optgroups:[],plugins:[],delimiter:",",splitOn:null,persist:!0,diacritics:!0,create:!1,createOnBlur:!1,createFilter:null,highlight:!0,openOnFocus:!0,maxOptions:1e3,maxItems:null,hideSelected:null,addPrecedence:!1,selectOnTab:!1,preload:!1,allowEmptyOption:!1,closeAfterSelect:!1,scrollDuration:60,loadThrottle:300,loadingClass:"loading",dataAttr:"data-data",optgroupField:"optgroup",valueField:"value",labelField:"text",optgroupLabelField:"label",optgroupValueField:"value",lockOptgroupOrder:!1,sortField:"$order",searchField:["text"],searchConjunction:"and",mode:null,wrapperClass:"selectize-control",inputClass:"selectize-input",dropdownClass:"selectize-dropdown",dropdownContentClass:"selectize-dropdown-content",dropdownParent:null,copyClassesToDropdown:!0,render:{}},a.fn.selectize=function(b){var c=a.fn.selectize.defaults,d=a.extend({},c,b),e=d.dataAttr,f=d.labelField,g=d.valueField,h=d.optgroupField,i=d.optgroupLabelField,j=d.optgroupValueField,k=function(b,c){var h,i,j,k,l=b.attr(e);if(l)for(c.options=JSON.parse(l),h=0,i=c.options.length;h<i;h++)c.items.push(c.options[h][g]);else{var m=a.trim(b.val()||"");if(!d.allowEmptyOption&&!m.length)return;for(j=m.split(d.delimiter),h=0,i=j.length;h<i;h++)k={},k[f]=j[h],k[g]=j[h],c.options.push(k);c.items=j}},l=function(b,c){var k,l,m,n,o=c.options,p={},q=function(a){var b=e&&a.attr(e);return"string"==typeof b&&b.length?JSON.parse(b):null},r=function(b,e){b=a(b);var i=z(b.val());if(i||d.allowEmptyOption)if(p.hasOwnProperty(i)){if(e){var j=p[i][h];j?a.isArray(j)?j.push(e):p[i][h]=[j,e]:p[i][h]=e}}else{var k=q(b)||{};k[f]=k[f]||b.text(),k[g]=k[g]||i,k[h]=k[h]||e,p[i]=k,o.push(k),b.is(":selected")&&c.items.push(i)}},s=function(b){var d,e,f,g,h;for(b=a(b),f=b.attr("label"),f&&(g=q(b)||{},g[i]=f,g[j]=f,c.optgroups.push(g)),h=a("option",b),d=0,e=h.length;d<e;d++)r(h[d],f)};for(c.maxItems=b.attr("multiple")?null:1,n=b.children(),k=0,l=n.length;k<l;k++)m=n[k].tagName.toLowerCase(),"optgroup"===m?s(n[k]):"option"===m&&r(n[k])};return this.each(function(){if(!this.selectize){var e,f=a(this),g=this.tagName.toLowerCase(),h=f.attr("placeholder")||f.attr("data-placeholder");h||d.allowEmptyOption||(h=f.children('option[value=""]').text());var i={placeholder:h,options:[],optgroups:[],items:[]};"select"===g?l(f,i):k(f,i),e=new L(f,a.extend(!0,{},c,i,b))}})},a.fn.selectize.defaults=L.defaults,a.fn.selectize.support={validity:x},L.define("drag_drop",function(b){if(!a.fn.sortable)throw new Error('The "drag_drop" plugin requires jQuery UI "sortable".');if("multi"===this.settings.mode){var c=this;c.lock=function(){var a=c.lock;return function(){var b=c.$control.data("sortable");return b&&b.disable(),a.apply(c,arguments)}}(),c.unlock=function(){var a=c.unlock;return function(){var b=c.$control.data("sortable");return b&&b.enable(),a.apply(c,arguments)}}(),c.setup=function(){var b=c.setup;return function(){b.apply(this,arguments);var d=c.$control.sortable({items:"[data-value]",forcePlaceholderSize:!0,disabled:c.isLocked,start:function(a,b){b.placeholder.css("width",b.helper.css("width")),d.css({overflow:"visible"})},stop:function(){d.css({overflow:"hidden"});var b=c.$activeItems?c.$activeItems.slice():null,e=[];d.children("[data-value]").each(function(){e.push(a(this).attr("data-value"))}),c.setValue(e),c.setActiveItem(b)}})}}()}}),L.define("dropdown_header",function(b){var c=this;b=a.extend({title:"Untitled",headerClass:"selectize-dropdown-header",titleRowClass:"selectize-dropdown-header-title",labelClass:"selectize-dropdown-header-label",closeClass:"selectize-dropdown-header-close",html:function(a){return'<div class="'+a.headerClass+'"><div class="'+a.titleRowClass+'"><span class="'+a.labelClass+'">'+a.title+'</span><a href="javascript:void(0)" class="'+a.closeClass+'">&times;</a></div></div>'}},b),c.setup=function(){var d=c.setup;return function(){d.apply(c,arguments),c.$dropdown_header=a(b.html(b)),c.$dropdown.prepend(c.$dropdown_header)}}()}),L.define("optgroup_columns",function(b){var c=this;b=a.extend({equalizeWidth:!0,equalizeHeight:!0},b),this.getAdjacentOption=function(b,c){var d=b.closest("[data-group]").find("[data-selectable]"),e=d.index(b)+c;return e>=0&&e<d.length?d.eq(e):a()},this.onKeyDown=function(){var a=c.onKeyDown;return function(b){var d,e,f,g;return!this.isOpen||b.keyCode!==j&&b.keyCode!==m?a.apply(this,arguments):(c.ignoreHover=!0,g=this.$activeOption.closest("[data-group]"),d=g.find("[data-selectable]").index(this.$activeOption),g=b.keyCode===j?g.prev("[data-group]"):g.next("[data-group]"),f=g.find("[data-selectable]"),e=f.eq(Math.min(f.length-1,d)),void(e.length&&this.setActiveOption(e)))}}();var d=function(){var a,b=d.width,c=document;return"undefined"==typeof b&&(a=c.createElement("div"),a.innerHTML='<div style="width:50px;height:50px;position:absolute;left:-50px;top:-50px;overflow:auto;"><div style="width:1px;height:100px;"></div></div>',a=a.firstChild,c.body.appendChild(a),b=d.width=a.offsetWidth-a.clientWidth,c.body.removeChild(a)),b},e=function(){var e,f,g,h,i,j,k;if(k=a("[data-group]",c.$dropdown_content),f=k.length,f&&c.$dropdown_content.width()){if(b.equalizeHeight){for(g=0,e=0;e<f;e++)g=Math.max(g,k.eq(e).height());k.css({height:g})}b.equalizeWidth&&(j=c.$dropdown_content.innerWidth()-d(),h=Math.round(j/f),k.css({width:h}),f>1&&(i=j-h*(f-1),k.eq(f-1).css({width:i})))}};(b.equalizeHeight||b.equalizeWidth)&&(B.after(this,"positionDropdown",e),B.after(this,"refreshOptions",e))}),L.define("remove_button",function(b){b=a.extend({label:"&times;",title:"Remove",className:"remove",append:!0},b);var c=function(b,c){c.className="remove-single";var d=b,e='<a href="javascript:void(0)" class="'+c.className+'" tabindex="-1" title="'+A(c.title)+'">'+c.label+"</a>",f=function(a,b){return a+b};b.setup=function(){var g=d.setup;return function(){if(c.append){var h=a(d.$input.context).attr("id"),i=(a("#"+h),d.settings.render.item);d.settings.render.item=function(a){return f(i.apply(b,arguments),e)}}g.apply(b,arguments),b.$control.on("click","."+c.className,function(a){a.preventDefault(),d.isLocked||d.clear()})}}()},d=function(b,c){var d=b,e='<a href="javascript:void(0)" class="'+c.className+'" tabindex="-1" title="'+A(c.title)+'">'+c.label+"</a>",f=function(a,b){var c=a.search(/(<\/[^>]+>\s*)$/);return a.substring(0,c)+b+a.substring(c)};b.setup=function(){var g=d.setup;return function(){if(c.append){var h=d.settings.render.item;d.settings.render.item=function(a){return f(h.apply(b,arguments),e)}}g.apply(b,arguments),b.$control.on("click","."+c.className,function(b){if(b.preventDefault(),!d.isLocked){var c=a(b.currentTarget).parent();d.setActiveItem(c),d.deleteSelection()&&d.setCaret(d.items.length)}})}}()};return"single"===this.settings.mode?void c(this,b):void d(this,b)}),L.define("restore_on_backspace",function(a){var b=this;a.text=a.text||function(a){return a[this.settings.labelField]},this.onKeyDown=function(){var c=b.onKeyDown;return function(b){var d,e;return b.keyCode===p&&""===this.$control_input.val()&&!this.$activeItems.length&&(d=this.caretPos-1,d>=0&&d<this.items.length)?(e=this.options[this.items[d]],this.deleteSelection(b)&&(this.setTextboxValue(a.text.apply(this,[e])),this.refreshOptions(!0)),void b.preventDefault()):c.apply(this,arguments)}}()}),L}); \ No newline at end of file
+/*! selectize.js - v0.12.4 | https://github.com/selectize/selectize.js | Apache License (v2) */
+!function(a,b){"function"==typeof define&&define.amd?define("sifter",b):"object"==typeof exports?module.exports=b():a.Sifter=b()}(this,function(){var a=function(a,b){this.items=a,this.settings=b||{diacritics:!0}};a.prototype.tokenize=function(a){if(a=e(String(a||"").toLowerCase()),!a||!a.length)return[];var b,c,d,g,i=[],j=a.split(/ +/);for(b=0,c=j.length;b<c;b++){if(d=f(j[b]),this.settings.diacritics)for(g in h)h.hasOwnProperty(g)&&(d=d.replace(new RegExp(g,"g"),h[g]));i.push({string:j[b],regex:new RegExp(d,"i")})}return i},a.prototype.iterator=function(a,b){var c;c=g(a)?Array.prototype.forEach||function(a){for(var b=0,c=this.length;b<c;b++)a(this[b],b,this)}:function(a){for(var b in this)this.hasOwnProperty(b)&&a(this[b],b,this)},c.apply(a,[b])},a.prototype.getScoreFunction=function(a,b){var c,e,f,g,h;c=this,a=c.prepareSearch(a,b),f=a.tokens,e=a.options.fields,g=f.length,h=a.options.nesting;var i=function(a,b){var c,d;return a?(a=String(a||""),d=a.search(b.regex),d===-1?0:(c=b.string.length/a.length,0===d&&(c+=.5),c)):0},j=function(){var a=e.length;return a?1===a?function(a,b){return i(d(b,e[0],h),a)}:function(b,c){for(var f=0,g=0;f<a;f++)g+=i(d(c,e[f],h),b);return g/a}:function(){return 0}}();return g?1===g?function(a){return j(f[0],a)}:"and"===a.options.conjunction?function(a){for(var b,c=0,d=0;c<g;c++){if(b=j(f[c],a),b<=0)return 0;d+=b}return d/g}:function(a){for(var b=0,c=0;b<g;b++)c+=j(f[b],a);return c/g}:function(){return 0}},a.prototype.getSortFunction=function(a,c){var e,f,g,h,i,j,k,l,m,n,o;if(g=this,a=g.prepareSearch(a,c),o=!a.query&&c.sort_empty||c.sort,m=function(a,b){return"$score"===a?b.score:d(g.items[b.id],a,c.nesting)},i=[],o)for(e=0,f=o.length;e<f;e++)(a.query||"$score"!==o[e].field)&&i.push(o[e]);if(a.query){for(n=!0,e=0,f=i.length;e<f;e++)if("$score"===i[e].field){n=!1;break}n&&i.unshift({field:"$score",direction:"desc"})}else for(e=0,f=i.length;e<f;e++)if("$score"===i[e].field){i.splice(e,1);break}for(l=[],e=0,f=i.length;e<f;e++)l.push("desc"===i[e].direction?-1:1);return j=i.length,j?1===j?(h=i[0].field,k=l[0],function(a,c){return k*b(m(h,a),m(h,c))}):function(a,c){var d,e,f;for(d=0;d<j;d++)if(f=i[d].field,e=l[d]*b(m(f,a),m(f,c)))return e;return 0}:null},a.prototype.prepareSearch=function(a,b){if("object"==typeof a)return a;b=c({},b);var d=b.fields,e=b.sort,f=b.sort_empty;return d&&!g(d)&&(b.fields=[d]),e&&!g(e)&&(b.sort=[e]),f&&!g(f)&&(b.sort_empty=[f]),{options:b,query:String(a||"").toLowerCase(),tokens:this.tokenize(a),total:0,items:[]}},a.prototype.search=function(a,b){var c,d,e,f,g=this;return d=this.prepareSearch(a,b),b=d.options,a=d.query,f=b.score||g.getScoreFunction(d),a.length?g.iterator(g.items,function(a,e){c=f(a),(b.filter===!1||c>0)&&d.items.push({score:c,id:e})}):g.iterator(g.items,function(a,b){d.items.push({score:1,id:b})}),e=g.getSortFunction(d,b),e&&d.items.sort(e),d.total=d.items.length,"number"==typeof b.limit&&(d.items=d.items.slice(0,b.limit)),d};var b=function(a,b){return"number"==typeof a&&"number"==typeof b?a>b?1:a<b?-1:0:(a=i(String(a||"")),b=i(String(b||"")),a>b?1:b>a?-1:0)},c=function(a,b){var c,d,e,f;for(c=1,d=arguments.length;c<d;c++)if(f=arguments[c])for(e in f)f.hasOwnProperty(e)&&(a[e]=f[e]);return a},d=function(a,b,c){if(a&&b){if(!c)return a[b];for(var d=b.split(".");d.length&&(a=a[d.shift()]););return a}},e=function(a){return(a+"").replace(/^\s+|\s+$|/g,"")},f=function(a){return(a+"").replace(/([.?*+^$[\]\\(){}|-])/g,"\\$1")},g=Array.isArray||"undefined"!=typeof $&&$.isArray||function(a){return"[object Array]"===Object.prototype.toString.call(a)},h={a:"[aḀḁĂăÂâǍǎȺⱥȦȧẠạÄäÀàÁáĀāÃãÅåąĄÃąĄ]",b:"[b␢βΒB฿𐌁ᛒ]",c:"[cĆćĈĉČčĊċC̄c̄ÇçḈḉȻȼƇƈɕᴄCc]",d:"[dĎďḊḋḐḑḌḍḒḓḎḏĐđD̦d̦ƉɖƊɗƋƌᵭᶁᶑȡᴅDdð]",e:"[eÉéÈèÊêḘḙĚěĔĕẼẽḚḛẺẻĖėËëĒēȨȩĘęᶒɆɇȄȅẾếỀềỄễỂểḜḝḖḗḔḕȆȇẸẹỆệⱸᴇEeɘǝƏƐε]",f:"[fƑƒḞḟ]",g:"[gɢ₲ǤǥĜĝĞğĢģƓɠĠġ]",h:"[hĤĥĦħḨḩẖẖḤḥḢḣɦʰǶƕ]",i:"[iÍíÌìĬĭÎîǏǐÏïḮḯĨĩĮįĪīỈỉȈȉȊȋỊịḬḭƗɨɨ̆ᵻᶖİiIıɪIi]",j:"[jȷĴĵɈɉʝɟʲ]",k:"[kƘƙꝀꝁḰḱǨǩḲḳḴḵκϰ₭]",l:"[lŁłĽľĻļĹĺḶḷḸḹḼḽḺḻĿŀȽƚⱠⱡⱢɫɬᶅɭȴʟLl]",n:"[nŃńǸǹŇňÑñṄṅŅņṆṇṊṋṈṉN̈n̈ƝɲȠƞᵰᶇɳȵɴNnŊŋ]",o:"[oØøÖöÓóÒòÔôǑǒŐőŎŏȮȯỌọƟɵƠơỎỏŌōÕõǪǫȌȍՕօ]",p:"[pṔṕṖṗⱣᵽƤƥᵱ]",q:"[qꝖꝗʠɊɋꝘꝙq̃]",r:"[rŔŕɌɍŘřŖŗṘṙȐȑȒȓṚṛⱤɽ]",s:"[sŚśṠṡṢṣꞨꞩŜŝŠšŞşȘșS̈s̈]",t:"[tŤťṪṫŢţṬṭƮʈȚțṰṱṮṯƬƭ]",u:"[uŬŭɄʉỤụÜüÚúÙùÛûǓǔŰűŬŭƯưỦủŪūŨũŲųȔȕ∪]",v:"[vṼṽṾṿƲʋꝞꝟⱱʋ]",w:"[wẂẃẀẁŴŵẄẅẆẇẈẉ]",x:"[xẌẍẊẋχ]",y:"[yÝýỲỳŶŷŸÿỸỹẎẏỴỵɎɏƳƴ]",z:"[zŹźẐẑŽžŻżẒẓẔẕƵƶ]"},i=function(){var a,b,c,d,e="",f={};for(c in h)if(h.hasOwnProperty(c))for(d=h[c].substring(2,h[c].length-1),e+=d,a=0,b=d.length;a<b;a++)f[d.charAt(a)]=c;var g=new RegExp("["+e+"]","g");return function(a){return a.replace(g,function(a){return f[a]}).toLowerCase()}}();return a}),function(a,b){"function"==typeof define&&define.amd?define("microplugin",b):"object"==typeof exports?module.exports=b():a.MicroPlugin=b()}(this,function(){var a={};a.mixin=function(a){a.plugins={},a.prototype.initializePlugins=function(a){var c,d,e,f=this,g=[];if(f.plugins={names:[],settings:{},requested:{},loaded:{}},b.isArray(a))for(c=0,d=a.length;c<d;c++)"string"==typeof a[c]?g.push(a[c]):(f.plugins.settings[a[c].name]=a[c].options,g.push(a[c].name));else if(a)for(e in a)a.hasOwnProperty(e)&&(f.plugins.settings[e]=a[e],g.push(e));for(;g.length;)f.require(g.shift())},a.prototype.loadPlugin=function(b){var c=this,d=c.plugins,e=a.plugins[b];if(!a.plugins.hasOwnProperty(b))throw new Error('Unable to find "'+b+'" plugin');d.requested[b]=!0,d.loaded[b]=e.fn.apply(c,[c.plugins.settings[b]||{}]),d.names.push(b)},a.prototype.require=function(a){var b=this,c=b.plugins;if(!b.plugins.loaded.hasOwnProperty(a)){if(c.requested[a])throw new Error('Plugin has circular dependency ("'+a+'")');b.loadPlugin(a)}return c.loaded[a]},a.define=function(b,c){a.plugins[b]={name:b,fn:c}}};var b={isArray:Array.isArray||function(a){return"[object Array]"===Object.prototype.toString.call(a)}};return a}),function(a,b){"function"==typeof define&&define.amd?define("selectize",["jquery","sifter","microplugin"],b):"object"==typeof exports?module.exports=b(require("jquery"),require("sifter"),require("microplugin")):a.Selectize=b(a.jQuery,a.Sifter,a.MicroPlugin)}(this,function(a,b,c){"use strict";var d=function(a,b){if("string"!=typeof b||b.length){var c="string"==typeof b?new RegExp(b,"i"):b,d=function(a){var b=0;if(3===a.nodeType){var e=a.data.search(c);if(e>=0&&a.data.length>0){var f=a.data.match(c),g=document.createElement("span");g.className="highlight";var h=a.splitText(e),i=(h.splitText(f[0].length),h.cloneNode(!0));g.appendChild(i),h.parentNode.replaceChild(g,h),b=1}}else if(1===a.nodeType&&a.childNodes&&!/(script|style)/i.test(a.tagName))for(var j=0;j<a.childNodes.length;++j)j+=d(a.childNodes[j]);return b};return a.each(function(){d(this)})}};a.fn.removeHighlight=function(){return this.find("span.highlight").each(function(){this.parentNode.firstChild.nodeName;var a=this.parentNode;a.replaceChild(this.firstChild,this),a.normalize()}).end()};var e=function(){};e.prototype={on:function(a,b){this._events=this._events||{},this._events[a]=this._events[a]||[],this._events[a].push(b)},off:function(a,b){var c=arguments.length;return 0===c?delete this._events:1===c?delete this._events[a]:(this._events=this._events||{},void(a in this._events!=!1&&this._events[a].splice(this._events[a].indexOf(b),1)))},trigger:function(a){if(this._events=this._events||{},a in this._events!=!1)for(var b=0;b<this._events[a].length;b++)this._events[a][b].apply(this,Array.prototype.slice.call(arguments,1))}},e.mixin=function(a){for(var b=["on","off","trigger"],c=0;c<b.length;c++)a.prototype[b[c]]=e.prototype[b[c]]};var f=/Mac/.test(navigator.userAgent),g=65,h=13,i=27,j=37,k=38,l=80,m=39,n=40,o=78,p=8,q=46,r=16,s=f?91:17,t=f?18:17,u=9,v=1,w=2,x=!/android/i.test(window.navigator.userAgent)&&!!document.createElement("input").validity,y=function(a){return"undefined"!=typeof a},z=function(a){return"undefined"==typeof a||null===a?null:"boolean"==typeof a?a?"1":"0":a+""},A=function(a){return(a+"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},B={};B.before=function(a,b,c){var d=a[b];a[b]=function(){return c.apply(a,arguments),d.apply(a,arguments)}},B.after=function(a,b,c){var d=a[b];a[b]=function(){var b=d.apply(a,arguments);return c.apply(a,arguments),b}};var C=function(a){var b=!1;return function(){b||(b=!0,a.apply(this,arguments))}},D=function(a,b){var c;return function(){var d=this,e=arguments;window.clearTimeout(c),c=window.setTimeout(function(){a.apply(d,e)},b)}},E=function(a,b,c){var d,e=a.trigger,f={};a.trigger=function(){var c=arguments[0];return b.indexOf(c)===-1?e.apply(a,arguments):void(f[c]=arguments)},c.apply(a,[]),a.trigger=e;for(d in f)f.hasOwnProperty(d)&&e.apply(a,f[d])},F=function(a,b,c,d){a.on(b,c,function(b){for(var c=b.target;c&&c.parentNode!==a[0];)c=c.parentNode;return b.currentTarget=c,d.apply(this,[b])})},G=function(a){var b={};if("selectionStart"in a)b.start=a.selectionStart,b.length=a.selectionEnd-b.start;else if(document.selection){a.focus();var c=document.selection.createRange(),d=document.selection.createRange().text.length;c.moveStart("character",-a.value.length),b.start=c.text.length-d,b.length=d}return b},H=function(a,b,c){var d,e,f={};if(c)for(d=0,e=c.length;d<e;d++)f[c[d]]=a.css(c[d]);else f=a.css();b.css(f)},I=function(b,c){if(!b)return 0;var d=a("<test>").css({position:"absolute",top:-99999,left:-99999,width:"auto",padding:0,whiteSpace:"pre"}).text(b).appendTo("body");H(c,d,["letterSpacing","fontSize","fontFamily","fontWeight","textTransform"]);var e=d.width();return d.remove(),e},J=function(a){var b=null,c=function(c,d){var e,f,g,h,i,j,k,l;c=c||window.event||{},d=d||{},c.metaKey||c.altKey||(d.force||a.data("grow")!==!1)&&(e=a.val(),c.type&&"keydown"===c.type.toLowerCase()&&(f=c.keyCode,g=f>=97&&f<=122||f>=65&&f<=90||f>=48&&f<=57||32===f,f===q||f===p?(l=G(a[0]),l.length?e=e.substring(0,l.start)+e.substring(l.start+l.length):f===p&&l.start?e=e.substring(0,l.start-1)+e.substring(l.start+1):f===q&&"undefined"!=typeof l.start&&(e=e.substring(0,l.start)+e.substring(l.start+1))):g&&(j=c.shiftKey,k=String.fromCharCode(c.keyCode),k=j?k.toUpperCase():k.toLowerCase(),e+=k)),h=a.attr("placeholder"),!e&&h&&(e=h),i=I(e,a)+4,i!==b&&(b=i,a.width(i),a.triggerHandler("resize")))};a.on("keydown keyup update blur",c),c()},K=function(a){var b=document.createElement("div");return b.appendChild(a.cloneNode(!0)),b.innerHTML},L=function(a,b){b||(b={});var c="Selectize";console.error(c+": "+a),b.explanation&&(console.group&&console.group(),console.error(b.explanation),console.group&&console.groupEnd())},M=function(c,d){var e,f,g,h,i=this;h=c[0],h.selectize=i;var j=window.getComputedStyle&&window.getComputedStyle(h,null);if(g=j?j.getPropertyValue("direction"):h.currentStyle&&h.currentStyle.direction,g=g||c.parents("[dir]:first").attr("dir")||"",a.extend(i,{order:0,settings:d,$input:c,tabIndex:c.attr("tabindex")||"",tagType:"select"===h.tagName.toLowerCase()?v:w,rtl:/rtl/i.test(g),eventNS:".selectize"+ ++M.count,highlightedValue:null,isOpen:!1,isDisabled:!1,isRequired:c.is("[required]"),isInvalid:!1,isLocked:!1,isFocused:!1,isInputHidden:!1,isSetup:!1,isShiftDown:!1,isCmdDown:!1,isCtrlDown:!1,ignoreFocus:!1,ignoreBlur:!1,ignoreHover:!1,hasOptions:!1,currentResults:null,lastValue:"",caretPos:0,loading:0,loadedSearches:{},$activeOption:null,$activeItems:[],optgroups:{},options:{},userOptions:{},items:[],renderCache:{},onSearchChange:null===d.loadThrottle?i.onSearchChange:D(i.onSearchChange,d.loadThrottle)}),i.sifter=new b(this.options,{diacritics:d.diacritics}),i.settings.options){for(e=0,f=i.settings.options.length;e<f;e++)i.registerOption(i.settings.options[e]);delete i.settings.options}if(i.settings.optgroups){for(e=0,f=i.settings.optgroups.length;e<f;e++)i.registerOptionGroup(i.settings.optgroups[e]);delete i.settings.optgroups}i.settings.mode=i.settings.mode||(1===i.settings.maxItems?"single":"multi"),"boolean"!=typeof i.settings.hideSelected&&(i.settings.hideSelected="multi"===i.settings.mode),i.initializePlugins(i.settings.plugins),i.setupCallbacks(),i.setupTemplates(),i.setup()};return e.mixin(M),"undefined"!=typeof c?c.mixin(M):L("Dependency MicroPlugin is missing",{explanation:'Make sure you either: (1) are using the "standalone" version of Selectize, or (2) require MicroPlugin before you load Selectize.'}),a.extend(M.prototype,{setup:function(){var b,c,d,e,g,h,i,j,k,l,m=this,n=m.settings,o=m.eventNS,p=a(window),q=a(document),u=m.$input;if(i=m.settings.mode,j=u.attr("class")||"",b=a("<div>").addClass(n.wrapperClass).addClass(j).addClass(i),c=a("<div>").addClass(n.inputClass).addClass("items").appendTo(b),d=a('<input type="text" autocomplete="off" />').appendTo(c).attr("tabindex",u.is(":disabled")?"-1":m.tabIndex),h=a(n.dropdownParent||b),e=a("<div>").addClass(n.dropdownClass).addClass(i).hide().appendTo(h),g=a("<div>").addClass(n.dropdownContentClass).appendTo(e),(l=u.attr("id"))&&(d.attr("id",l+"-selectized"),a("label[for='"+l+"']").attr("for",l+"-selectized")),m.settings.copyClassesToDropdown&&e.addClass(j),b.css({width:u[0].style.width}),m.plugins.names.length&&(k="plugin-"+m.plugins.names.join(" plugin-"),b.addClass(k),e.addClass(k)),(null===n.maxItems||n.maxItems>1)&&m.tagType===v&&u.attr("multiple","multiple"),m.settings.placeholder&&d.attr("placeholder",n.placeholder),!m.settings.splitOn&&m.settings.delimiter){var w=m.settings.delimiter.replace(/[-\/\\^$*+?.()|[\]{}]/g,"\\$&");m.settings.splitOn=new RegExp("\\s*"+w+"+\\s*")}u.attr("autocorrect")&&d.attr("autocorrect",u.attr("autocorrect")),u.attr("autocapitalize")&&d.attr("autocapitalize",u.attr("autocapitalize")),m.$wrapper=b,m.$control=c,m.$control_input=d,m.$dropdown=e,m.$dropdown_content=g,e.on("mouseenter","[data-selectable]",function(){return m.onOptionHover.apply(m,arguments)}),e.on("mousedown click","[data-selectable]",function(){return m.onOptionSelect.apply(m,arguments)}),F(c,"mousedown","*:not(input)",function(){return m.onItemSelect.apply(m,arguments)}),J(d),c.on({mousedown:function(){return m.onMouseDown.apply(m,arguments)},click:function(){return m.onClick.apply(m,arguments)}}),d.on({mousedown:function(a){a.stopPropagation()},keydown:function(){return m.onKeyDown.apply(m,arguments)},keyup:function(){return m.onKeyUp.apply(m,arguments)},keypress:function(){return m.onKeyPress.apply(m,arguments)},resize:function(){m.positionDropdown.apply(m,[])},blur:function(){return m.onBlur.apply(m,arguments)},focus:function(){return m.ignoreBlur=!1,m.onFocus.apply(m,arguments)},paste:function(){return m.onPaste.apply(m,arguments)}}),q.on("keydown"+o,function(a){m.isCmdDown=a[f?"metaKey":"ctrlKey"],m.isCtrlDown=a[f?"altKey":"ctrlKey"],m.isShiftDown=a.shiftKey}),q.on("keyup"+o,function(a){a.keyCode===t&&(m.isCtrlDown=!1),a.keyCode===r&&(m.isShiftDown=!1),a.keyCode===s&&(m.isCmdDown=!1)}),q.on("mousedown"+o,function(a){if(m.isFocused){if(a.target===m.$dropdown[0]||a.target.parentNode===m.$dropdown[0])return!1;m.$control.has(a.target).length||a.target===m.$control[0]||m.blur(a.target)}}),p.on(["scroll"+o,"resize"+o].join(" "),function(){m.isOpen&&m.positionDropdown.apply(m,arguments)}),p.on("mousemove"+o,function(){m.ignoreHover=!1}),this.revertSettings={$children:u.children().detach(),tabindex:u.attr("tabindex")},u.attr("tabindex",-1).hide().after(m.$wrapper),a.isArray(n.items)&&(m.setValue(n.items),delete n.items),x&&u.on("invalid"+o,function(a){a.preventDefault(),m.isInvalid=!0,m.refreshState()}),m.updateOriginalInput(),m.refreshItems(),m.refreshState(),m.updatePlaceholder(),m.isSetup=!0,u.is(":disabled")&&m.disable(),m.on("change",this.onChange),u.data("selectize",m),u.addClass("selectized"),m.trigger("initialize"),n.preload===!0&&m.onSearchChange("")},setupTemplates:function(){var b=this,c=b.settings.labelField,d=b.settings.optgroupLabelField,e={optgroup:function(a){return'<div class="optgroup">'+a.html+"</div>"},optgroup_header:function(a,b){return'<div class="optgroup-header">'+b(a[d])+"</div>"},option:function(a,b){return'<div class="option">'+b(a[c])+"</div>"},item:function(a,b){return'<div class="item">'+b(a[c])+"</div>"},option_create:function(a,b){return'<div class="create">Add <strong>'+b(a.input)+"</strong>&hellip;</div>"}};b.settings.render=a.extend({},e,b.settings.render)},setupCallbacks:function(){var a,b,c={initialize:"onInitialize",change:"onChange",item_add:"onItemAdd",item_remove:"onItemRemove",clear:"onClear",option_add:"onOptionAdd",option_remove:"onOptionRemove",option_clear:"onOptionClear",optgroup_add:"onOptionGroupAdd",optgroup_remove:"onOptionGroupRemove",optgroup_clear:"onOptionGroupClear",dropdown_open:"onDropdownOpen",dropdown_close:"onDropdownClose",type:"onType",load:"onLoad",focus:"onFocus",blur:"onBlur"};for(a in c)c.hasOwnProperty(a)&&(b=this.settings[c[a]],b&&this.on(a,b))},onClick:function(a){var b=this;b.isFocused||(b.focus(),a.preventDefault())},onMouseDown:function(b){var c=this,d=b.isDefaultPrevented();a(b.target);if(c.isFocused){if(b.target!==c.$control_input[0])return"single"===c.settings.mode?c.isOpen?c.close():c.open():d||c.setActiveItem(null),!1}else d||window.setTimeout(function(){c.focus()},0)},onChange:function(){this.$input.trigger("change")},onPaste:function(b){var c=this;return c.isFull()||c.isInputHidden||c.isLocked?void b.preventDefault():void(c.settings.splitOn&&setTimeout(function(){var b=c.$control_input.val();if(b.match(c.settings.splitOn))for(var d=a.trim(b).split(c.settings.splitOn),e=0,f=d.length;e<f;e++)c.createItem(d[e])},0))},onKeyPress:function(a){if(this.isLocked)return a&&a.preventDefault();var b=String.fromCharCode(a.keyCode||a.which);return this.settings.create&&"multi"===this.settings.mode&&b===this.settings.delimiter?(this.createItem(),a.preventDefault(),!1):void 0},onKeyDown:function(a){var b=(a.target===this.$control_input[0],this);if(b.isLocked)return void(a.keyCode!==u&&a.preventDefault());switch(a.keyCode){case g:if(b.isCmdDown)return void b.selectAll();break;case i:return void(b.isOpen&&(a.preventDefault(),a.stopPropagation(),b.close()));case o:if(!a.ctrlKey||a.altKey)break;case n:if(!b.isOpen&&b.hasOptions)b.open();else if(b.$activeOption){b.ignoreHover=!0;var c=b.getAdjacentOption(b.$activeOption,1);c.length&&b.setActiveOption(c,!0,!0)}return void a.preventDefault();case l:if(!a.ctrlKey||a.altKey)break;case k:if(b.$activeOption){b.ignoreHover=!0;var d=b.getAdjacentOption(b.$activeOption,-1);d.length&&b.setActiveOption(d,!0,!0)}return void a.preventDefault();case h:return void(b.isOpen&&b.$activeOption&&(b.onOptionSelect({currentTarget:b.$activeOption}),a.preventDefault()));case j:return void b.advanceSelection(-1,a);case m:return void b.advanceSelection(1,a);case u:return b.settings.selectOnTab&&b.isOpen&&b.$activeOption&&(b.onOptionSelect({currentTarget:b.$activeOption}),b.isFull()||a.preventDefault()),void(b.settings.create&&b.createItem()&&a.preventDefault());case p:case q:return void b.deleteSelection(a)}return!b.isFull()&&!b.isInputHidden||(f?a.metaKey:a.ctrlKey)?void 0:void a.preventDefault()},onKeyUp:function(a){var b=this;if(b.isLocked)return a&&a.preventDefault();var c=b.$control_input.val()||"";b.lastValue!==c&&(b.lastValue=c,b.onSearchChange(c),b.refreshOptions(),b.trigger("type",c))},onSearchChange:function(a){var b=this,c=b.settings.load;c&&(b.loadedSearches.hasOwnProperty(a)||(b.loadedSearches[a]=!0,b.load(function(d){c.apply(b,[a,d])})))},onFocus:function(a){var b=this,c=b.isFocused;return b.isDisabled?(b.blur(),a&&a.preventDefault(),!1):void(b.ignoreFocus||(b.isFocused=!0,"focus"===b.settings.preload&&b.onSearchChange(""),c||b.trigger("focus"),b.$activeItems.length||(b.showInput(),b.setActiveItem(null),b.refreshOptions(!!b.settings.openOnFocus)),b.refreshState()))},onBlur:function(a,b){var c=this;if(c.isFocused&&(c.isFocused=!1,!c.ignoreFocus)){if(!c.ignoreBlur&&document.activeElement===c.$dropdown_content[0])return c.ignoreBlur=!0,void c.onFocus(a);var d=function(){c.close(),c.setTextboxValue(""),c.setActiveItem(null),c.setActiveOption(null),c.setCaret(c.items.length),c.refreshState(),b&&b.focus&&b.focus(),c.ignoreFocus=!1,c.trigger("blur")};c.ignoreFocus=!0,c.settings.create&&c.settings.createOnBlur?c.createItem(null,!1,d):d()}},onOptionHover:function(a){this.ignoreHover||this.setActiveOption(a.currentTarget,!1)},onOptionSelect:function(b){var c,d,e=this;b.preventDefault&&(b.preventDefault(),b.stopPropagation()),d=a(b.currentTarget),d.hasClass("create")?e.createItem(null,function(){e.settings.closeAfterSelect&&e.close()}):(c=d.attr("data-value"),"undefined"!=typeof c&&(e.lastQuery=null,e.setTextboxValue(""),e.addItem(c),e.settings.closeAfterSelect?e.close():!e.settings.hideSelected&&b.type&&/mouse/.test(b.type)&&e.setActiveOption(e.getOption(c))))},onItemSelect:function(a){var b=this;b.isLocked||"multi"===b.settings.mode&&(a.preventDefault(),b.setActiveItem(a.currentTarget,a))},load:function(a){var b=this,c=b.$wrapper.addClass(b.settings.loadingClass);b.loading++,a.apply(b,[function(a){b.loading=Math.max(b.loading-1,0),a&&a.length&&(b.addOption(a),b.refreshOptions(b.isFocused&&!b.isInputHidden)),b.loading||c.removeClass(b.settings.loadingClass),b.trigger("load",a)}])},setTextboxValue:function(a){var b=this.$control_input,c=b.val()!==a;c&&(b.val(a).triggerHandler("update"),this.lastValue=a)},getValue:function(){return this.tagType===v&&this.$input.attr("multiple")?this.items:this.items.join(this.settings.delimiter)},setValue:function(a,b){var c=b?[]:["change"];E(this,c,function(){this.clear(b),this.addItems(a,b)})},setActiveItem:function(b,c){var d,e,f,g,h,i,j,k,l=this;if("single"!==l.settings.mode){if(b=a(b),!b.length)return a(l.$activeItems).removeClass("active"),l.$activeItems=[],void(l.isFocused&&l.showInput());if(d=c&&c.type.toLowerCase(),"mousedown"===d&&l.isShiftDown&&l.$activeItems.length){for(k=l.$control.children(".active:last"),g=Array.prototype.indexOf.apply(l.$control[0].childNodes,[k[0]]),h=Array.prototype.indexOf.apply(l.$control[0].childNodes,[b[0]]),g>h&&(j=g,g=h,h=j),e=g;e<=h;e++)i=l.$control[0].childNodes[e],l.$activeItems.indexOf(i)===-1&&(a(i).addClass("active"),l.$activeItems.push(i));c.preventDefault()}else"mousedown"===d&&l.isCtrlDown||"keydown"===d&&this.isShiftDown?b.hasClass("active")?(f=l.$activeItems.indexOf(b[0]),l.$activeItems.splice(f,1),b.removeClass("active")):l.$activeItems.push(b.addClass("active")[0]):(a(l.$activeItems).removeClass("active"),l.$activeItems=[b.addClass("active")[0]]);l.hideInput(),this.isFocused||l.focus()}},setActiveOption:function(b,c,d){var e,f,g,h,i,j=this;j.$activeOption&&j.$activeOption.removeClass("active"),j.$activeOption=null,b=a(b),b.length&&(j.$activeOption=b.addClass("active"),!c&&y(c)||(e=j.$dropdown_content.height(),f=j.$activeOption.outerHeight(!0),c=j.$dropdown_content.scrollTop()||0,g=j.$activeOption.offset().top-j.$dropdown_content.offset().top+c,h=g,i=g-e+f,g+f>e+c?j.$dropdown_content.stop().animate({scrollTop:i},d?j.settings.scrollDuration:0):g<c&&j.$dropdown_content.stop().animate({scrollTop:h},d?j.settings.scrollDuration:0)))},selectAll:function(){var a=this;"single"!==a.settings.mode&&(a.$activeItems=Array.prototype.slice.apply(a.$control.children(":not(input)").addClass("active")),a.$activeItems.length&&(a.hideInput(),a.close()),a.focus())},hideInput:function(){var a=this;a.setTextboxValue(""),a.$control_input.css({opacity:0,position:"absolute",left:a.rtl?1e4:-1e4}),a.isInputHidden=!0},showInput:function(){this.$control_input.css({opacity:1,position:"relative",left:0}),this.isInputHidden=!1},focus:function(){var a=this;a.isDisabled||(a.ignoreFocus=!0,a.$control_input[0].focus(),window.setTimeout(function(){a.ignoreFocus=!1,a.onFocus()},0))},blur:function(a){this.$control_input[0].blur(),this.onBlur(null,a)},getScoreFunction:function(a){return this.sifter.getScoreFunction(a,this.getSearchOptions())},getSearchOptions:function(){var a=this.settings,b=a.sortField;return"string"==typeof b&&(b=[{field:b}]),{fields:a.searchField,conjunction:a.searchConjunction,sort:b}},search:function(b){var c,d,e,f=this,g=f.settings,h=this.getSearchOptions();if(g.score&&(e=f.settings.score.apply(this,[b]),"function"!=typeof e))throw new Error('Selectize "score" setting must be a function that returns a function');if(b!==f.lastQuery?(f.lastQuery=b,d=f.sifter.search(b,a.extend(h,{score:e})),f.currentResults=d):d=a.extend(!0,{},f.currentResults),g.hideSelected)for(c=d.items.length-1;c>=0;c--)f.items.indexOf(z(d.items[c].id))!==-1&&d.items.splice(c,1);return d},refreshOptions:function(b){var c,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s;"undefined"==typeof b&&(b=!0);var t=this,u=a.trim(t.$control_input.val()),v=t.search(u),w=t.$dropdown_content,x=t.$activeOption&&z(t.$activeOption.attr("data-value"));for(g=v.items.length,"number"==typeof t.settings.maxOptions&&(g=Math.min(g,t.settings.maxOptions)),h={},i=[],c=0;c<g;c++)for(j=t.options[v.items[c].id],k=t.render("option",j),l=j[t.settings.optgroupField]||"",m=a.isArray(l)?l:[l],e=0,f=m&&m.length;e<f;e++)l=m[e],t.optgroups.hasOwnProperty(l)||(l=""),h.hasOwnProperty(l)||(h[l]=document.createDocumentFragment(),i.push(l)),h[l].appendChild(k);for(this.settings.lockOptgroupOrder&&i.sort(function(a,b){var c=t.optgroups[a].$order||0,d=t.optgroups[b].$order||0;return c-d}),n=document.createDocumentFragment(),c=0,g=i.length;c<g;c++)l=i[c],t.optgroups.hasOwnProperty(l)&&h[l].childNodes.length?(o=document.createDocumentFragment(),o.appendChild(t.render("optgroup_header",t.optgroups[l])),o.appendChild(h[l]),n.appendChild(t.render("optgroup",a.extend({},t.optgroups[l],{html:K(o),dom:o})))):n.appendChild(h[l]);if(w.html(n),t.settings.highlight&&v.query.length&&v.tokens.length)for(w.removeHighlight(),c=0,g=v.tokens.length;c<g;c++)d(w,v.tokens[c].regex);if(!t.settings.hideSelected)for(c=0,g=t.items.length;c<g;c++)t.getOption(t.items[c]).addClass("selected");p=t.canCreate(u),p&&(w.prepend(t.render("option_create",{input:u})),s=a(w[0].childNodes[0])),t.hasOptions=v.items.length>0||p,t.hasOptions?(v.items.length>0?(r=x&&t.getOption(x),r&&r.length?q=r:"single"===t.settings.mode&&t.items.length&&(q=t.getOption(t.items[0])),q&&q.length||(q=s&&!t.settings.addPrecedence?t.getAdjacentOption(s,1):w.find("[data-selectable]:first"))):q=s,t.setActiveOption(q),b&&!t.isOpen&&t.open()):(t.setActiveOption(null),b&&t.isOpen&&t.close())},addOption:function(b){var c,d,e,f=this;if(a.isArray(b))for(c=0,d=b.length;c<d;c++)f.addOption(b[c]);else(e=f.registerOption(b))&&(f.userOptions[e]=!0,f.lastQuery=null,f.trigger("option_add",e,b))},registerOption:function(a){var b=z(a[this.settings.valueField]);return"undefined"!=typeof b&&null!==b&&!this.options.hasOwnProperty(b)&&(a.$order=a.$order||++this.order,this.options[b]=a,b)},registerOptionGroup:function(a){var b=z(a[this.settings.optgroupValueField]);return!!b&&(a.$order=a.$order||++this.order,this.optgroups[b]=a,b)},addOptionGroup:function(a,b){b[this.settings.optgroupValueField]=a,(a=this.registerOptionGroup(b))&&this.trigger("optgroup_add",a,b)},removeOptionGroup:function(a){this.optgroups.hasOwnProperty(a)&&(delete this.optgroups[a],this.renderCache={},this.trigger("optgroup_remove",a))},clearOptionGroups:function(){this.optgroups={},this.renderCache={},this.trigger("optgroup_clear")},updateOption:function(b,c){var d,e,f,g,h,i,j,k=this;if(b=z(b),f=z(c[k.settings.valueField]),null!==b&&k.options.hasOwnProperty(b)){if("string"!=typeof f)throw new Error("Value must be set in option data");j=k.options[b].$order,f!==b&&(delete k.options[b],g=k.items.indexOf(b),g!==-1&&k.items.splice(g,1,f)),c.$order=c.$order||j,k.options[f]=c,h=k.renderCache.item,i=k.renderCache.option,h&&(delete h[b],delete h[f]),i&&(delete i[b],delete i[f]),k.items.indexOf(f)!==-1&&(d=k.getItem(b),e=a(k.render("item",c)),d.hasClass("active")&&e.addClass("active"),d.replaceWith(e)),k.lastQuery=null,k.isOpen&&k.refreshOptions(!1)}},removeOption:function(a,b){var c=this;a=z(a);var d=c.renderCache.item,e=c.renderCache.option;d&&delete d[a],e&&delete e[a],delete c.userOptions[a],delete c.options[a],c.lastQuery=null,c.trigger("option_remove",a),c.removeItem(a,b)},clearOptions:function(){var a=this;a.loadedSearches={},a.userOptions={},a.renderCache={},a.options=a.sifter.items={},a.lastQuery=null,a.trigger("option_clear"),a.clear()},getOption:function(a){return this.getElementWithValue(a,this.$dropdown_content.find("[data-selectable]"))},getAdjacentOption:function(b,c){var d=this.$dropdown.find("[data-selectable]"),e=d.index(b)+c;return e>=0&&e<d.length?d.eq(e):a()},getElementWithValue:function(b,c){if(b=z(b),"undefined"!=typeof b&&null!==b)for(var d=0,e=c.length;d<e;d++)if(c[d].getAttribute("data-value")===b)return a(c[d]);return a()},getItem:function(a){return this.getElementWithValue(a,this.$control.children())},addItems:function(b,c){for(var d=a.isArray(b)?b:[b],e=0,f=d.length;e<f;e++)this.isPending=e<f-1,this.addItem(d[e],c)},addItem:function(b,c){var d=c?[]:["change"];E(this,d,function(){var d,e,f,g,h,i=this,j=i.settings.mode;return b=z(b),i.items.indexOf(b)!==-1?void("single"===j&&i.close()):void(i.options.hasOwnProperty(b)&&("single"===j&&i.clear(c),"multi"===j&&i.isFull()||(d=a(i.render("item",i.options[b])),h=i.isFull(),i.items.splice(i.caretPos,0,b),i.insertAtCaret(d),(!i.isPending||!h&&i.isFull())&&i.refreshState(),i.isSetup&&(f=i.$dropdown_content.find("[data-selectable]"),i.isPending||(e=i.getOption(b),g=i.getAdjacentOption(e,1).attr("data-value"),i.refreshOptions(i.isFocused&&"single"!==j),g&&i.setActiveOption(i.getOption(g))),!f.length||i.isFull()?i.close():i.positionDropdown(),i.updatePlaceholder(),i.trigger("item_add",b,d),i.updateOriginalInput({silent:c})))))})},removeItem:function(b,c){var d,e,f,g=this;d=b instanceof a?b:g.getItem(b),b=z(d.attr("data-value")),e=g.items.indexOf(b),e!==-1&&(d.remove(),d.hasClass("active")&&(f=g.$activeItems.indexOf(d[0]),g.$activeItems.splice(f,1)),g.items.splice(e,1),g.lastQuery=null,!g.settings.persist&&g.userOptions.hasOwnProperty(b)&&g.removeOption(b,c),e<g.caretPos&&g.setCaret(g.caretPos-1),g.refreshState(),g.updatePlaceholder(),g.updateOriginalInput({silent:c}),g.positionDropdown(),g.trigger("item_remove",b,d))},createItem:function(b,c){var d=this,e=d.caretPos;b=b||a.trim(d.$control_input.val()||"");var f=arguments[arguments.length-1];if("function"!=typeof f&&(f=function(){}),"boolean"!=typeof c&&(c=!0),!d.canCreate(b))return f(),!1;d.lock();var g="function"==typeof d.settings.create?this.settings.create:function(a){var b={};return b[d.settings.labelField]=a,b[d.settings.valueField]=a,b},h=C(function(a){if(d.unlock(),!a||"object"!=typeof a)return f();var b=z(a[d.settings.valueField]);return"string"!=typeof b?f():(d.setTextboxValue(""),d.addOption(a),d.setCaret(e),d.addItem(b),d.refreshOptions(c&&"single"!==d.settings.mode),void f(a))}),i=g.apply(this,[b,h]);return"undefined"!=typeof i&&h(i),!0},refreshItems:function(){this.lastQuery=null,this.isSetup&&this.addItem(this.items),this.refreshState(),this.updateOriginalInput()},refreshState:function(){this.refreshValidityState(),this.refreshClasses()},refreshValidityState:function(){if(!this.isRequired)return!1;var a=!this.items.length;this.isInvalid=a,this.$control_input.prop("required",a),this.$input.prop("required",!a)},refreshClasses:function(){var b=this,c=b.isFull(),d=b.isLocked;b.$wrapper.toggleClass("rtl",b.rtl),b.$control.toggleClass("focus",b.isFocused).toggleClass("disabled",b.isDisabled).toggleClass("required",b.isRequired).toggleClass("invalid",b.isInvalid).toggleClass("locked",d).toggleClass("full",c).toggleClass("not-full",!c).toggleClass("input-active",b.isFocused&&!b.isInputHidden).toggleClass("dropdown-active",b.isOpen).toggleClass("has-options",!a.isEmptyObject(b.options)).toggleClass("has-items",b.items.length>0),b.$control_input.data("grow",!c&&!d)},isFull:function(){return null!==this.settings.maxItems&&this.items.length>=this.settings.maxItems},updateOriginalInput:function(a){var b,c,d,e,f=this;if(a=a||{},f.tagType===v){for(d=[],b=0,c=f.items.length;b<c;b++)e=f.options[f.items[b]][f.settings.labelField]||"",d.push('<option value="'+A(f.items[b])+'" selected="selected">'+A(e)+"</option>");d.length||this.$input.attr("multiple")||d.push('<option value="" selected="selected"></option>'),
+f.$input.html(d.join(""))}else f.$input.val(f.getValue()),f.$input.attr("value",f.$input.val());f.isSetup&&(a.silent||f.trigger("change",f.$input.val()))},updatePlaceholder:function(){if(this.settings.placeholder){var a=this.$control_input;this.items.length?a.removeAttr("placeholder"):a.attr("placeholder",this.settings.placeholder),a.triggerHandler("update",{force:!0})}},open:function(){var a=this;a.isLocked||a.isOpen||"multi"===a.settings.mode&&a.isFull()||(a.focus(),a.isOpen=!0,a.refreshState(),a.$dropdown.css({visibility:"hidden",display:"block"}),a.positionDropdown(),a.$dropdown.css({visibility:"visible"}),a.trigger("dropdown_open",a.$dropdown))},close:function(){var a=this,b=a.isOpen;"single"===a.settings.mode&&a.items.length&&(a.hideInput(),a.$control_input.blur()),a.isOpen=!1,a.$dropdown.hide(),a.setActiveOption(null),a.refreshState(),b&&a.trigger("dropdown_close",a.$dropdown)},positionDropdown:function(){var a=this.$control,b="body"===this.settings.dropdownParent?a.offset():a.position();b.top+=a.outerHeight(!0),this.$dropdown.css({width:a.outerWidth(),top:b.top,left:b.left})},clear:function(a){var b=this;b.items.length&&(b.$control.children(":not(input)").remove(),b.items=[],b.lastQuery=null,b.setCaret(0),b.setActiveItem(null),b.updatePlaceholder(),b.updateOriginalInput({silent:a}),b.refreshState(),b.showInput(),b.trigger("clear"))},insertAtCaret:function(b){var c=Math.min(this.caretPos,this.items.length);0===c?this.$control.prepend(b):a(this.$control[0].childNodes[c]).before(b),this.setCaret(c+1)},deleteSelection:function(b){var c,d,e,f,g,h,i,j,k,l=this;if(e=b&&b.keyCode===p?-1:1,f=G(l.$control_input[0]),l.$activeOption&&!l.settings.hideSelected&&(i=l.getAdjacentOption(l.$activeOption,-1).attr("data-value")),g=[],l.$activeItems.length){for(k=l.$control.children(".active:"+(e>0?"last":"first")),h=l.$control.children(":not(input)").index(k),e>0&&h++,c=0,d=l.$activeItems.length;c<d;c++)g.push(a(l.$activeItems[c]).attr("data-value"));b&&(b.preventDefault(),b.stopPropagation())}else(l.isFocused||"single"===l.settings.mode)&&l.items.length&&(e<0&&0===f.start&&0===f.length?g.push(l.items[l.caretPos-1]):e>0&&f.start===l.$control_input.val().length&&g.push(l.items[l.caretPos]));if(!g.length||"function"==typeof l.settings.onDelete&&l.settings.onDelete.apply(l,[g])===!1)return!1;for("undefined"!=typeof h&&l.setCaret(h);g.length;)l.removeItem(g.pop());return l.showInput(),l.positionDropdown(),l.refreshOptions(!0),i&&(j=l.getOption(i),j.length&&l.setActiveOption(j)),!0},advanceSelection:function(a,b){var c,d,e,f,g,h,i=this;0!==a&&(i.rtl&&(a*=-1),c=a>0?"last":"first",d=G(i.$control_input[0]),i.isFocused&&!i.isInputHidden?(f=i.$control_input.val().length,g=a<0?0===d.start&&0===d.length:d.start===f,g&&!f&&i.advanceCaret(a,b)):(h=i.$control.children(".active:"+c),h.length&&(e=i.$control.children(":not(input)").index(h),i.setActiveItem(null),i.setCaret(a>0?e+1:e))))},advanceCaret:function(a,b){var c,d,e=this;0!==a&&(c=a>0?"next":"prev",e.isShiftDown?(d=e.$control_input[c](),d.length&&(e.hideInput(),e.setActiveItem(d),b&&b.preventDefault())):e.setCaret(e.caretPos+a))},setCaret:function(b){var c=this;if(b="single"===c.settings.mode?c.items.length:Math.max(0,Math.min(c.items.length,b)),!c.isPending){var d,e,f,g;for(f=c.$control.children(":not(input)"),d=0,e=f.length;d<e;d++)g=a(f[d]).detach(),d<b?c.$control_input.before(g):c.$control.append(g)}c.caretPos=b},lock:function(){this.close(),this.isLocked=!0,this.refreshState()},unlock:function(){this.isLocked=!1,this.refreshState()},disable:function(){var a=this;a.$input.prop("disabled",!0),a.$control_input.prop("disabled",!0).prop("tabindex",-1),a.isDisabled=!0,a.lock()},enable:function(){var a=this;a.$input.prop("disabled",!1),a.$control_input.prop("disabled",!1).prop("tabindex",a.tabIndex),a.isDisabled=!1,a.unlock()},destroy:function(){var b=this,c=b.eventNS,d=b.revertSettings;b.trigger("destroy"),b.off(),b.$wrapper.remove(),b.$dropdown.remove(),b.$input.html("").append(d.$children).removeAttr("tabindex").removeClass("selectized").attr({tabindex:d.tabindex}).show(),b.$control_input.removeData("grow"),b.$input.removeData("selectize"),a(window).off(c),a(document).off(c),a(document.body).off(c),delete b.$input[0].selectize},render:function(b,c){var d,e,f="",g=!1,h=this;return"option"!==b&&"item"!==b||(d=z(c[h.settings.valueField]),g=!!d),g&&(y(h.renderCache[b])||(h.renderCache[b]={}),h.renderCache[b].hasOwnProperty(d))?h.renderCache[b][d]:(f=a(h.settings.render[b].apply(this,[c,A])),"option"===b||"option_create"===b?f.attr("data-selectable",""):"optgroup"===b&&(e=c[h.settings.optgroupValueField]||"",f.attr("data-group",e)),"option"!==b&&"item"!==b||f.attr("data-value",d||""),g&&(h.renderCache[b][d]=f[0]),f[0])},clearCache:function(a){var b=this;"undefined"==typeof a?b.renderCache={}:delete b.renderCache[a]},canCreate:function(a){var b=this;if(!b.settings.create)return!1;var c=b.settings.createFilter;return a.length&&("function"!=typeof c||c.apply(b,[a]))&&("string"!=typeof c||new RegExp(c).test(a))&&(!(c instanceof RegExp)||c.test(a))}}),M.count=0,M.defaults={options:[],optgroups:[],plugins:[],delimiter:",",splitOn:null,persist:!0,diacritics:!0,create:!1,createOnBlur:!1,createFilter:null,highlight:!0,openOnFocus:!0,maxOptions:1e3,maxItems:null,hideSelected:null,addPrecedence:!1,selectOnTab:!1,preload:!1,allowEmptyOption:!1,closeAfterSelect:!1,scrollDuration:60,loadThrottle:300,loadingClass:"loading",dataAttr:"data-data",optgroupField:"optgroup",valueField:"value",labelField:"text",optgroupLabelField:"label",optgroupValueField:"value",lockOptgroupOrder:!1,sortField:"$order",searchField:["text"],searchConjunction:"and",mode:null,wrapperClass:"selectize-control",inputClass:"selectize-input",dropdownClass:"selectize-dropdown",dropdownContentClass:"selectize-dropdown-content",dropdownParent:null,copyClassesToDropdown:!0,render:{}},a.fn.selectize=function(b){var c=a.fn.selectize.defaults,d=a.extend({},c,b),e=d.dataAttr,f=d.labelField,g=d.valueField,h=d.optgroupField,i=d.optgroupLabelField,j=d.optgroupValueField,k=function(b,c){var h,i,j,k,l=b.attr(e);if(l)for(c.options=JSON.parse(l),h=0,i=c.options.length;h<i;h++)c.items.push(c.options[h][g]);else{var m=a.trim(b.val()||"");if(!d.allowEmptyOption&&!m.length)return;for(j=m.split(d.delimiter),h=0,i=j.length;h<i;h++)k={},k[f]=j[h],k[g]=j[h],c.options.push(k);c.items=j}},l=function(b,c){var k,l,m,n,o=c.options,p={},q=function(a){var b=e&&a.attr(e);return"string"==typeof b&&b.length?JSON.parse(b):null},r=function(b,e){b=a(b);var i=z(b.val());if(i||d.allowEmptyOption)if(p.hasOwnProperty(i)){if(e){var j=p[i][h];j?a.isArray(j)?j.push(e):p[i][h]=[j,e]:p[i][h]=e}}else{var k=q(b)||{};k[f]=k[f]||b.text(),k[g]=k[g]||i,k[h]=k[h]||e,p[i]=k,o.push(k),b.is(":selected")&&c.items.push(i)}},s=function(b){var d,e,f,g,h;for(b=a(b),f=b.attr("label"),f&&(g=q(b)||{},g[i]=f,g[j]=f,c.optgroups.push(g)),h=a("option",b),d=0,e=h.length;d<e;d++)r(h[d],f)};for(c.maxItems=b.attr("multiple")?null:1,n=b.children(),k=0,l=n.length;k<l;k++)m=n[k].tagName.toLowerCase(),"optgroup"===m?s(n[k]):"option"===m&&r(n[k])};return this.each(function(){if(!this.selectize){var e,f=a(this),g=this.tagName.toLowerCase(),h=f.attr("placeholder")||f.attr("data-placeholder");h||d.allowEmptyOption||(h=f.children('option[value=""]').text());var i={placeholder:h,options:[],optgroups:[],items:[]};"select"===g?l(f,i):k(f,i),e=new M(f,a.extend(!0,{},c,i,b))}})},a.fn.selectize.defaults=M.defaults,a.fn.selectize.support={validity:x},M.define("drag_drop",function(b){if(!a.fn.sortable)throw new Error('The "drag_drop" plugin requires jQuery UI "sortable".');if("multi"===this.settings.mode){var c=this;c.lock=function(){var a=c.lock;return function(){var b=c.$control.data("sortable");return b&&b.disable(),a.apply(c,arguments)}}(),c.unlock=function(){var a=c.unlock;return function(){var b=c.$control.data("sortable");return b&&b.enable(),a.apply(c,arguments)}}(),c.setup=function(){var b=c.setup;return function(){b.apply(this,arguments);var d=c.$control.sortable({items:"[data-value]",forcePlaceholderSize:!0,disabled:c.isLocked,start:function(a,b){b.placeholder.css("width",b.helper.css("width")),d.css({overflow:"visible"})},stop:function(){d.css({overflow:"hidden"});var b=c.$activeItems?c.$activeItems.slice():null,e=[];d.children("[data-value]").each(function(){e.push(a(this).attr("data-value"))}),c.setValue(e),c.setActiveItem(b)}})}}()}}),M.define("dropdown_header",function(b){var c=this;b=a.extend({title:"Untitled",headerClass:"selectize-dropdown-header",titleRowClass:"selectize-dropdown-header-title",labelClass:"selectize-dropdown-header-label",closeClass:"selectize-dropdown-header-close",html:function(a){return'<div class="'+a.headerClass+'"><div class="'+a.titleRowClass+'"><span class="'+a.labelClass+'">'+a.title+'</span><a href="javascript:void(0)" class="'+a.closeClass+'">&times;</a></div></div>'}},b),c.setup=function(){var d=c.setup;return function(){d.apply(c,arguments),c.$dropdown_header=a(b.html(b)),c.$dropdown.prepend(c.$dropdown_header)}}()}),M.define("optgroup_columns",function(b){var c=this;b=a.extend({equalizeWidth:!0,equalizeHeight:!0},b),this.getAdjacentOption=function(b,c){var d=b.closest("[data-group]").find("[data-selectable]"),e=d.index(b)+c;return e>=0&&e<d.length?d.eq(e):a()},this.onKeyDown=function(){var a=c.onKeyDown;return function(b){var d,e,f,g;return!this.isOpen||b.keyCode!==j&&b.keyCode!==m?a.apply(this,arguments):(c.ignoreHover=!0,g=this.$activeOption.closest("[data-group]"),d=g.find("[data-selectable]").index(this.$activeOption),g=b.keyCode===j?g.prev("[data-group]"):g.next("[data-group]"),f=g.find("[data-selectable]"),e=f.eq(Math.min(f.length-1,d)),void(e.length&&this.setActiveOption(e)))}}();var d=function(){var a,b=d.width,c=document;return"undefined"==typeof b&&(a=c.createElement("div"),a.innerHTML='<div style="width:50px;height:50px;position:absolute;left:-50px;top:-50px;overflow:auto;"><div style="width:1px;height:100px;"></div></div>',a=a.firstChild,c.body.appendChild(a),b=d.width=a.offsetWidth-a.clientWidth,c.body.removeChild(a)),b},e=function(){var e,f,g,h,i,j,k;if(k=a("[data-group]",c.$dropdown_content),f=k.length,f&&c.$dropdown_content.width()){if(b.equalizeHeight){for(g=0,e=0;e<f;e++)g=Math.max(g,k.eq(e).height());k.css({height:g})}b.equalizeWidth&&(j=c.$dropdown_content.innerWidth()-d(),h=Math.round(j/f),k.css({width:h}),f>1&&(i=j-h*(f-1),k.eq(f-1).css({width:i})))}};(b.equalizeHeight||b.equalizeWidth)&&(B.after(this,"positionDropdown",e),B.after(this,"refreshOptions",e))}),M.define("remove_button",function(b){b=a.extend({label:"&times;",title:"Remove",className:"remove",append:!0},b);var c=function(b,c){c.className="remove-single";var d=b,e='<a href="javascript:void(0)" class="'+c.className+'" tabindex="-1" title="'+A(c.title)+'">'+c.label+"</a>",f=function(a,b){return a+b};b.setup=function(){var g=d.setup;return function(){if(c.append){var h=a(d.$input.context).attr("id"),i=(a("#"+h),d.settings.render.item);d.settings.render.item=function(a){return f(i.apply(b,arguments),e)}}g.apply(b,arguments),b.$control.on("click","."+c.className,function(a){a.preventDefault(),d.isLocked||d.clear()})}}()},d=function(b,c){var d=b,e='<a href="javascript:void(0)" class="'+c.className+'" tabindex="-1" title="'+A(c.title)+'">'+c.label+"</a>",f=function(a,b){var c=a.search(/(<\/[^>]+>\s*)$/);return a.substring(0,c)+b+a.substring(c)};b.setup=function(){var g=d.setup;return function(){if(c.append){var h=d.settings.render.item;d.settings.render.item=function(a){return f(h.apply(b,arguments),e)}}g.apply(b,arguments),b.$control.on("click","."+c.className,function(b){if(b.preventDefault(),!d.isLocked){var c=a(b.currentTarget).parent();d.setActiveItem(c),d.deleteSelection()&&d.setCaret(d.items.length)}})}}()};return"single"===this.settings.mode?void c(this,b):void d(this,b)}),M.define("restore_on_backspace",function(a){var b=this;a.text=a.text||function(a){return a[this.settings.labelField]},this.onKeyDown=function(){var c=b.onKeyDown;return function(b){var d,e;return b.keyCode===p&&""===this.$control_input.val()&&!this.$activeItems.length&&(d=this.caretPos-1,d>=0&&d<this.items.length)?(e=this.options[this.items[d]],this.deleteSelection(b)&&(this.setTextboxValue(a.text.apply(this,[e])),this.refreshOptions(!0)),void b.preventDefault()):c.apply(this,arguments)}}()}),M}); \ No newline at end of file
diff --git a/modules-available/js_selectize/style.css b/modules-available/js_selectize/style.css
index c5a8510c..799a4114 100644
--- a/modules-available/js_selectize/style.css
+++ b/modules-available/js_selectize/style.css
@@ -1,5 +1,5 @@
/**
- * selectize.bootstrap3.css (v0.12.2) - Bootstrap 3 Theme
+ * selectize.bootstrap3.css (v0.12.4) - Bootstrap 3 Theme
* Copyright (c) 2013–2015 Brian Reavis & contributors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
@@ -282,6 +282,7 @@
overflow-y: auto;
overflow-x: hidden;
max-height: 200px;
+ -webkit-overflow-scrolling: touch;
}
.selectize-control.single .selectize-input,
.selectize-control.single .selectize-input input {
diff --git a/modules-available/locations/inc/location.inc.php b/modules-available/locations/inc/location.inc.php
index bde07e61..73080094 100644
--- a/modules-available/locations/inc/location.inc.php
+++ b/modules-available/locations/inc/location.inc.php
@@ -6,6 +6,7 @@ class Location
private static $flatLocationCache = false;
private static $assocLocationCache = false;
private static $treeCache = false;
+ private static $subnetMapCache = false;
private static function getTree()
{
@@ -57,15 +58,22 @@ class Location
$output = array();
foreach ($tree as $node) {
$output[(int)$node['locationid']] = array(
+ 'locationid' => (int)$node['locationid'],
'parentlocationid' => (int)$node['parentlocationid'],
'parents' => $parents,
'locationname' => $node['locationname'],
- 'depth' => $depth
+ 'depth' => $depth,
+ 'isleaf' => true,
);
if (!empty($node['children'])) {
$output += self::flattenTreeAssoc($node['children'], array_merge($parents, array((int)$node['locationid'])), $depth + 1);
}
}
+ foreach ($output as &$entry) {
+ if (!isset($output[$entry['parentlocationid']]))
+ continue;
+ $output[$entry['parentlocationid']]['isleaf'] = false;
+ }
return $output;
}
@@ -185,33 +193,31 @@ class Location
if (Module::get('statistics') === false)
return false;
$ret = Database::queryFirst("SELECT locationid FROM machine WHERE machineuuid = :uuid", compact('uuid'));
- if ($ret === false)
+ if ($ret === false || !$ret['locationid'])
return false;
return (int)$ret['locationid'];
}
/**
* Get closest location by matching subnets. Deepest match in tree wins.
+ * Ignores any manually assigned locationid (fixedlocationid).
*
* @param string $ip IP address of client
* @return bool|int locationid, or false if no match
*/
public static function getFromIp($ip)
{
- $locationId = false;
- $long = sprintf('%u', ip2long($ip));
- $net = Database::simpleQuery('SELECT locationid FROM subnet'
- . ' WHERE :ip BETWEEN startaddr AND endaddr', array('ip' => $long));
- while ($row = $net->fetch(PDO::FETCH_ASSOC)) {
- $locations = self::getLocationsAssoc();
- $id = (int)$row['locationid'];
- if (!isset($locations[$id]))
- continue;
- if ($locationId !== false && $locations[$id]['depth'] <= $locations[$locationId]['depth'])
- continue;
- $locationId = $id;
+ if (Module::get('statistics') !== false) {
+ // Shortcut - try to use subnetlocationid in machine table
+ $ret = Database::queryFirst("SELECT subnetlocationid FROM machine WHERE clientip = :ip", compact('ip'));
+ if ($ret !== false) {
+ if ($ret['subnetlocationid'] > 0) {
+ return (int)$ret['subnetlocationid'];
+ }
+ return false;
+ }
}
- return $locationId;
+ return self::mapIpToLocation($ip);
}
/**
@@ -230,7 +236,9 @@ class Location
if ($ipLoc !== false && $uuid !== false) {
// Machine ip maps to a location, and we have a client supplied uuid
$uuidLoc = self::getFromMachineUuid($uuid);
- if ($uuidLoc !== false) {
+ if ($uuidLoc === $ipLoc) {
+ $locationId = $uuidLoc;
+ } else if ($uuidLoc !== false) {
// Validate that the location the IP maps to is in the chain we get using the
// location determined by the uuid
$uuidLocations = self::getLocationRootChain($uuidLoc);
@@ -285,40 +293,13 @@ class Location
return $subnets;
}
- /**
- * @return array|bool assoc array mapping from locationid to subnets
- */
- public static function getSubnetsByLocation(&$overlapSelf, &$overlapOther, $recursive = true)
+ public static function getOverlappingSubnets(&$overlapSelf = false, &$overlapOther = false)
{
+ if ($overlapSelf === false && $overlapOther === false) {
+ return;
+ }
$locs = self::getLocationsAssoc();
$subnets = self::getSubnets();
- // Find locations having nets overlapping with themselves if array was passed
- if ($overlapSelf === true || $overlapOther === true) {
- self::findOverlap($locs, $subnets, $overlapSelf, $overlapOther);
- }
- // Accumulate - copy up subnet definitions
- foreach ($locs as &$loc) {
- $loc['subnets'] = array();
- }
- unset($loc);
- foreach ($subnets as $subnet) {
- $lid = $subnet['locationid'];
- while (isset($locs[$lid])) {
- $locs[$lid]['subnets'][] = array(
- 'startaddr' => $subnet['startaddr'],
- 'endaddr' => $subnet['endaddr']
- );
- if (!$recursive)
- break;
- $lid = $locs[$lid]['parentlocationid'];
- }
- }
- return $locs;
- }
-
-
- private static function findOverlap($locs, $subnets, &$overlapSelf, &$overlapOther)
- {
if ($overlapSelf) {
$self = array();
}
@@ -368,6 +349,84 @@ class Location
}
}
+ /**
+ * @return array|bool assoc array mapping from locationid to subnets
+ */
+ public static function getSubnetsByLocation($recursive = false)
+ {
+ $locs = self::getLocationsAssoc();
+ $subnets = self::getSubnets();
+ // Accumulate - copy up subnet definitions
+ foreach ($locs as &$loc) {
+ $loc['subnets'] = array();
+ }
+ unset($loc);
+ foreach ($subnets as $subnet) {
+ $lid = $subnet['locationid'];
+ while (isset($locs[$lid])) {
+ $locs[$lid]['subnets'][] = array(
+ 'startaddr' => $subnet['startaddr'],
+ 'endaddr' => $subnet['endaddr']
+ );
+ if (!$recursive)
+ break;
+ $lid = $locs[$lid]['parentlocationid'];
+ }
+ }
+ return $locs;
+ }
+
+ /**
+ * Lookup $ip in subnets, try to find one that matches
+ * and return its locationid.
+ * If two+ subnets match, the one which is nested deeper wins.
+ * If two+ subnets match and have the same depth, the one which
+ * is smaller wins.
+ * If two+ subnets match and have the same depth and size, a
+ * random one will be returned.
+ *
+ * @param $ip IP to look up
+ * @return bool|int locationid ip matches, false = no match
+ */
+ public static function mapIpToLocation($ip)
+ {
+ if (self::$subnetMapCache === false) {
+ self::$subnetMapCache = self::getSubnetsByLocation();
+ }
+ $long = sprintf('%u', ip2long($ip));
+ $best = false;
+ $bestSize = 0;
+ foreach (self::$subnetMapCache as $lid => $data) {
+ if ($best !== false && self::$subnetMapCache[$lid]['depth'] < self::$subnetMapCache[$best]['depth'])
+ continue; // Don't even need to take a look
+ foreach ($data['subnets'] as $subnet) {
+ if ($long < $subnet['startaddr'] || $long > $subnet['endaddr'])
+ continue; // Nope
+ if ($best !== false // Already have a best candidate
+ && self::$subnetMapCache[$lid]['depth'] === self::$subnetMapCache[$best]['depth'] // Same depth
+ && $bestSize < $subnet['endaddr'] - $subnet['startaddr']) { // Old candidate has smaller subnet
+ // So we ignore this one as the old one is more specific
+ continue;
+ }
+ $bestSize = $subnet['endaddr'] - $subnet['startaddr'];
+ $best = $lid;
+ }
+ }
+ if ($best === false)
+ return false;
+ return (int)$best;
+ }
+
+ public static function updateMapIpToLocation($uuid, $ip)
+ {
+ $loc = self::mapIpToLocation($ip);
+ if ($loc === false) {
+ Database::exec("UPDATE machine SET subnetlocationid = NULL WHERE machineuuid = :uuid", compact('uuid'));
+ } else {
+ Database::exec("UPDATE machine SET subnetlocationid = :loc WHERE machineuuid = :uuid", compact('loc', 'uuid'));
+ }
+ }
+
private static function overlap($net1, $net2)
{
return ($net1['startaddr'] <= $net2['endaddr'] && $net1['endaddr'] >= $net2['startaddr']);
diff --git a/modules-available/locations/page.inc.php b/modules-available/locations/page.inc.php
index e002ae4c..26612099 100644
--- a/modules-available/locations/page.inc.php
+++ b/modules-available/locations/page.inc.php
@@ -26,6 +26,36 @@ class Page_Locations extends Page
}
}
+ private function updateAutoLocationId()
+ {
+ if (Module::get('statistics') === false)
+ return; // Nothing to do
+ $res = Database::simpleQuery("SELECT machineuuid, clientip FROM machine");
+ $updates = array();
+ $nulls = array();
+ while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
+ $loc = Location::mapIpToLocation($row['clientip']);
+ if ($loc === false) {
+ $nulls[] = $row['machineuuid'];
+ } else {
+ if (!isset($updates[$loc])) {
+ $updates[$loc] = array();
+ }
+ $updates[$loc][] = $row['machineuuid'];
+ }
+ }
+ error_log(print_r($updates, true));
+ if (!empty($nulls)) {
+ $qs = '?' . str_repeat(',?', count($nulls) - 1);
+ Database::exec("UPDATE machine SET subnetlocationid = NULL WHERE machineuuid IN ($qs)", $nulls);
+ }
+ foreach ($updates as $lid => $machines) {
+ $qs = '?' . str_repeat(',?', count($machines) - 1);
+ $lid = (int)$lid;
+ Database::exec("UPDATE machine SET subnetlocationid = $lid WHERE machineuuid IN ($qs)", $machines);
+ }
+ }
+
private function updateSubnets()
{
$count = 0;
@@ -55,6 +85,7 @@ class Page_Locations extends Page
$count += $stmt->rowCount();
}
}
+ $this->updateAutoLocationId();
Message::addSuccess('subnets-updated', $count);
Util::redirect('?do=Locations');
}
@@ -111,16 +142,22 @@ class Page_Locations extends Page
Message::addError('main.value-invalid', 'locationid', $locationId);
Util::redirect('?do=Locations');
}
+ $change = false;
// Delete location?
if ($locationId === $del) {
$this->deleteLocation($location);
+ $change = true;
}
// Update subnets
- $this->updateLocationSubnets();
+ $change |= $this->updateLocationSubnets();
// Insert subnets
- $this->addNewLocationSubnets($location);
+ $change |= $this->addNewLocationSubnets($location);
// Update location!
- $this->updateLocationData($location);
+ $change |= $this->updateLocationData($location);
+ if ($change) {
+ // In case subnets or tree layout changed, recalc this
+ $this->updateAutoLocationId();
+ }
Util::redirect('?do=Locations');
}
@@ -182,10 +219,12 @@ class Page_Locations extends Page
if ($ret > 0) {
Message::addSuccess('location-updated', $newName);
}
+ return $newParent != $location['parentlocationid'];
}
private function updateLocationSubnets()
{
+ $change = false;
// Deletion first
$dels = Request::post('deletesubnet', false);
if (is_array($dels)) {
@@ -200,13 +239,14 @@ class Page_Locations extends Page
}
if ($count > 0) {
Message::addInfo('subnets-deleted', $count);
+ $change = true;
}
}
// Now actual updates
$starts = Request::post('startaddr', false);
$ends = Request::post('endaddr', false);
if (!is_array($starts) || !is_array($ends)) {
- return;
+ return $change;
}
$count = 0;
$stmt = Database::prepare('UPDATE subnet SET startaddr = :start, endaddr = :end'
@@ -225,16 +265,19 @@ class Page_Locations extends Page
}
if ($count > 0) {
Message::addInfo('subnets-updated', $count);
+ $change = true;
}
+ return $change;
}
private function addNewLocationSubnets($location)
{
+ $change = false;
$locationId = (int)$location['locationid'];
$starts = Request::post('newstartaddr', false);
$ends = Request::post('newendaddr', false);
if (!is_array($starts) || !is_array($ends)) {
- return;
+ return $change;
}
$count = 0;
$stmt = Database::prepare('INSERT INTO subnet SET startaddr = :start, endaddr = :end, locationid = :location');
@@ -261,7 +304,9 @@ class Page_Locations extends Page
}
if ($count > 0) {
Message::addInfo('subnets-created', $count);
+ $change = true;
}
+ return $change;
}
/*
@@ -290,51 +335,44 @@ class Page_Locations extends Page
}
}
- private function queryMachineCount($lid, $subnets, $xtra = '')
- {
- if (!isset($subnets[$lid]))
- return 0;
- $loc =& $subnets[$lid];
- if (empty($loc['subnets'])) {
- $query = "SELECT Count(*) AS cnt FROM machine WHERE (locationid = :locationid)";
- } else {
- $query = "SELECT Count(*) AS cnt FROM machine WHERE (locationid = :locationid OR (locationid IS NULL AND (0";
- foreach ($loc['subnets'] as $sub) {
- $query .= ' OR INET_ATON(clientip) BETWEEN ' . $sub['startaddr'] . ' AND ' . $sub['endaddr'];
- }
- $query .= ')))';
- }
- if (!empty($xtra)) {
- $query .= ' ' . $xtra;
- }
- $ret = Database::queryFirst($query, array('locationid' => $lid));
- return $ret['cnt'];
- }
-
private function showLocationList()
{
+ // Warn admin about overlapping subnet definitions
$overlapSelf = $overlapOther = true;
- $subnetsFlat = Location::getSubnetsByLocation($overlapSelf, $overlapOther, false);
- $subnetsRecursive = Location::getSubnetsByLocation($overlapSelf, $overlapOther, true);
- $locs = Location::getLocations(0, 0, false, true);
+ Location::getOverlappingSubnets($overlapSelf, $overlapOther);
+ //$locs = Location::getLocations(0, 0, false, true);
+ $locs = Location::getLocationsAssoc();
// Statistics: Count machines for each subnet
$unassigned = false;
if (Module::get('statistics') !== false) {
$DL = time() - 605;
- foreach ($locs as &$location) {
- $lid = (int)$location['locationid'];
- $location['clientCount'] = $this->queryMachineCount($lid, $subnetsFlat);
- $location['clientCountSum'] = $this->queryMachineCount($lid, $subnetsRecursive);
- if ($location['clientCountSum'] > 0) {
- $location['clientLoad'] = round(($this->queryMachineCount($lid, $subnetsRecursive, "AND logintime <> 0 AND lastseen > $DL") / $location['clientCountSum']) * 100) . '%';
+ $unassigned = 0;
+ $res = Database::simpleQuery("SELECT locationid, Count(*) AS cnt, Sum(If(lastseen > $DL AND logintime <> 0, 1, 0)) AS used
+ FROM machine GROUP BY locationid");
+ while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
+ $loc = (int)$row['locationid'];
+ if (isset($locs[$loc])) {
+ $locs[$loc]['clientCount'] = $row['cnt'];
+ $locs[$loc]['clientLoad'] = round(100 * $row['used'] / $row['cnt']) . '%';
+ } else {
+ $unassigned += $row['cnt'];
+ }
+ }
+ unset($loc);
+ foreach ($locs as &$loc) {
+ if (!isset($loc['clientCount'])) {
+ $loc['clientCount'] = 0;
+ $loc['clientLoad'] = '0%';
+ }
+ $loc['clientCountSum'] = $loc['clientCount'];
+ }
+ unset($loc);
+ foreach ($locs as $loc) {
+ foreach ($loc['parents'] as $pid) {
+ $locs[(int)$pid]['clientCountSum'] += $loc['clientCount'];
}
}
- $res = Database::queryFirst("SELECT Count(*) AS cnt FROM machine m"
- . " LEFT JOIN subnet s ON (INET_ATON(m.clientip) BETWEEN s.startaddr AND s.endaddr)"
- . " WHERE m.locationid IS NULL AND s.locationid IS NULL");
- $unassigned = $res['cnt'];
}
- unset($loc, $location);
// Show currently active sysconfig for each location
$defaultConfig = false;
if (Module::isAvailable('sysconfig')) {
@@ -445,8 +483,7 @@ class Page_Locations extends Page
$count = $online = $used = 0;
if (Module::get('statistics') !== false) {
$mres = Database::simpleQuery("SELECT lastseen, logintime FROM machine"
- . " LEFT JOIN subnet ON (INET_ATON(machine.clientip) BETWEEN startaddr AND endaddr AND machine.locationid IS NULL)"
- . " WHERE (subnet.locationid = :lid) OR (machine.locationid = :lid)", array('lid' => $locationId));
+ . " WHERE machine.locationid = :lid", array('lid' => $locationId));
$DL = time() - 605;
while ($row = $mres->fetch(PDO::FETCH_ASSOC)) {
$count++;
diff --git a/modules-available/locations/templates/location-subnets.html b/modules-available/locations/templates/location-subnets.html
index 5da931e1..4eadeffd 100644
--- a/modules-available/locations/templates/location-subnets.html
+++ b/modules-available/locations/templates/location-subnets.html
@@ -1,4 +1,4 @@
-<td class="slx-well">
+<div class="slx-well">
<div class="slx-bold">{{lang_locationSettings}}</div>
<form method="post" action="?do=Locations">
<input type="hidden" name="token" value="{{token}}">
diff --git a/modules-available/locations/templates/locations.html b/modules-available/locations/templates/locations.html
index 9dcc6db1..c2dc610e 100644
--- a/modules-available/locations/templates/locations.html
+++ b/modules-available/locations/templates/locations.html
@@ -23,7 +23,7 @@
<tr>
<td>
<div style="display:inline-block;width:{{depth}}em"></div>
- <a href="#" onclick="slxOpenLocation(this, {{locationid}})">{{locationname}} <b class="caret"></b></a>
+ <a href="#" onclick="slxOpenLocation(this, {{locationid}}); return false">{{locationname}} <b class="caret"></b></a>
</td>
<td class="text-nowrap" align="right">
{{#havestatistics}}
@@ -134,7 +134,7 @@ function slxOpenLocation(e, lid) {
if (existing.is(slxLastLocation)) {
slxLastLocation = false;
} else {
- existing.show();
+ existing.show()[0].scrollIntoView();
$(e).closest('tr').addClass('active slx-bold');
slxLastLocation = existing;
}
@@ -146,6 +146,7 @@ function slxOpenLocation(e, lid) {
$(e).closest('tr').addClass('active slx-bold').after(tr);
td.load('?do=Locations&action=showlocation&locationid=' + lid, function() {
slxAttachCidr();
+ $('#location-details-' + lid)[0].scrollIntoView();
});
slxLastLocation = tr;
}
diff --git a/modules-available/roomplanner/clientscript.js b/modules-available/roomplanner/clientscript.js
index 4380b16c..53e0164a 100644
--- a/modules-available/roomplanner/clientscript.js
+++ b/modules-available/roomplanner/clientscript.js
@@ -5,13 +5,19 @@
* */
/* Map: uuid -> obj */
-machineCache = {};
+var machineCache = {};
-selectMachinInitialized = false;
-
-placedMachines = [];
+var selectMachinInitialized = false;
+var placedMachines = [];
+function makeCombinedField(machineArray)
+{
+ machineArray.forEach(function (v,i,a){
+ machineArray[i].combined = (v.machineuuid + " " + v.hostname + " " + v.clientip + " " + v.macaddr).toLocaleLowerCase();
+ });
+ return machineArray;
+}
function renderMachineEntry(item, escape) {
machineCache[item.machineuuid] = item;
@@ -20,46 +26,71 @@ function renderMachineEntry(item, escape) {
// console.log(placedMachines);
var isUsed = $.inArray(item.machineuuid, placedMachines) > -1;
- var extra = isUsed ? ' used ' : '';
+ var extraClass = '';
+ var extraText = '';
if (isUsed) {
- console.log('rendering used');
+ extraText = ' (already placed)';
+ extraClass = 'used';
+ } else if (item.otherroom) {
+ extraText = ' (in ' + item.otherroom + ')';
+ extraClass = 'used';
}
- return '<div class="machine-entry ' + extra +'">'
+ return '<div class="machine-entry ' + extraClass +'">'
//+ ' <div class="machine-logo"><i class="glyphicon glyphicon-hdd"></i></div>'
+ ' <div class="machine-body">'
- + ' <div class="machine-entry-header"> ' + escape(item.hostname) + (isUsed ? ' (already placed)' : '') + '</div>'
- + ' <table class="table table-sm">'
+ + ' <div class="machine-entry-header"> ' + escape(item.hostname) + extraText + '</div>'
+ + ' <table>'
+ '<tr><td>UUID:</td> <td>' + escape(item.machineuuid) + '</td></tr>'
- + '<tr><td>MAC:</td> <td>' + escape(item.macaddr) + '</td></tr>'
+ + '<tr><td>MAC: </td> <td>' + escape(item.macaddr) + '</td></tr>'
+ '<tr><td>IP: </td> <td>' + escape(item.clientip) + '</td></tr>'
+ ' </table>'
+ ' </div>'
+ '</div>';
}
+var queryCache = {};
+
+function filterCache(key, query) {
+ return queryCache[key].filter(function (el) {
+ return -1 !== el.combined.indexOf(query);
+ });
+}
+
function loadMachines(query, callback) {
console.log('queryMachines(' + query + ')');
- if (query.length < 2) return callback();
+ if (query.length < 2) {
+ callback();
+ return;
+ }
+ query = query.toLocaleLowerCase();
+ // See if we have a previous query in our cache that is a superset for this one
+ for (var k in queryCache) {
+ if (query.indexOf(k) !== -1) {
+ callback(filterCache(k, query));
+ return;
+ }
+ }
$.ajax({
url: '?do=roomplanner&action=getmachines&query=' + encodeURIComponent(query),
type: 'GET',
+ dataType: 'json',
error: function() {
console.log('error while doing ajax call');
callback();
},
- success: function(res) {
+ success: function(json) {
console.log('success ajax call');
- var json = JSON.parse(res);
- json.machines.forEach(function (v,i,a){
- a[i].combined = v.machineuuid + " " + v.hostname + " " + v.clientip + " " + v.macaddr;
- });
- return callback(json.machines);
+ var machines = makeCombinedField(json.machines);
+ // Server cuts off at 100, so only cache if it contains less entries, as
+ // the new, more specific query could return previously removed results.
+ if (machines.length < 100) {
+ queryCache[query] = machines;
+ }
+ callback(machines);
}
});
}
-
-
function clearSearchBox() {
$selectizeSearch[0].selectize.setValue([], true);
$selectizeSearch[0].selectize.clearCache();
@@ -85,7 +116,7 @@ function initSelectize() {
render : { option : renderMachineEntry, item: renderMachineEntry},
load: loadMachines,
maxItems: 1,
- sortField: 'clientip',
+ sortField: 'hostname',
sortDirection: 'asc',
onChange: clearSubnetBox
});
@@ -101,7 +132,7 @@ function initSelectize() {
create: false,
render : { option : renderMachineEntry, item: renderMachineEntry},
maxItems: 1,
- sortField: 'clientip',
+ sortField: 'hostname',
sortDirection: 'asc',
onChange: clearSearchBox
});
diff --git a/modules-available/roomplanner/inc/pvsgenerator.inc.php b/modules-available/roomplanner/inc/pvsgenerator.inc.php
index 063c748d..c00d5439 100644
--- a/modules-available/roomplanner/inc/pvsgenerator.inc.php
+++ b/modules-available/roomplanner/inc/pvsgenerator.inc.php
@@ -145,7 +145,7 @@ class PvsGenerator
private static function getMachines($roomid)
{
$ret = Database::simpleQuery(
- 'SELECT clientip, position FROM machine WHERE locationid = :locationid',
+ 'SELECT clientip, position FROM machine WHERE fixedlocationid = :locationid',
['locationid' => $roomid]);
$machines = array();
@@ -153,6 +153,9 @@ class PvsGenerator
while ($row = $ret->fetch(PDO::FETCH_ASSOC)) {
$position = json_decode($row['position'], true);
+ if ($position === false || !isset($position['gridRow']) || !isset($position['gridCol']))
+ continue; // TODO: Remove entry/set to NULL?
+
$machine = array();
$machine['clientip'] = $row['clientip'];
$machine['gridRow'] = $position['gridRow'];
diff --git a/modules-available/roomplanner/js/grid.js b/modules-available/roomplanner/js/grid.js
index 09e52099..cc09e21d 100644
--- a/modules-available/roomplanner/js/grid.js
+++ b/modules-available/roomplanner/js/grid.js
@@ -97,14 +97,14 @@ if (!roomplanner) var roomplanner = {
});
},
initTooltip: function(el) {
- if ($(el).attr('itemtype') == 'pc') {
+ if ($(el).attr('itemtype') === 'pc') {
var tip = "<b>Rechnerdaten</b><br>";
$(roomplanner.computerAttributes).each(function(i,key){
tip += __(key)+": "+$(el).attr(key)+"<br>";
});
- $(el).attr('title', tip);
- $(el).tooltip({html: true});
+ $(el).attr('title', tip).attr('data-toggle', 'tooltip');
+ $(el).tooltip({html: true, container: 'body'});
}
},
/**
diff --git a/modules-available/roomplanner/lang/de/messages.json b/modules-available/roomplanner/lang/de/messages.json
index 48edf483..3943c2c3 100644
--- a/modules-available/roomplanner/lang/de/messages.json
+++ b/modules-available/roomplanner/lang/de/messages.json
@@ -1,4 +1,5 @@
{
+ "invalid-tutor-uuid": "Ung\u00fcltige ID f\u00fcr den Tutor festgelegt",
"json-data-invalid": "\u00dcbermittelte Daten sind kein g\u00fcltiges JSON",
"need-locationid": "Keine locationid angegeben"
} \ No newline at end of file
diff --git a/modules-available/roomplanner/lang/en/messages.json b/modules-available/roomplanner/lang/en/messages.json
index 1e4d3a1b..b43fc951 100644
--- a/modules-available/roomplanner/lang/en/messages.json
+++ b/modules-available/roomplanner/lang/en/messages.json
@@ -1,4 +1,5 @@
{
+ "invalid-tutor-uuid": "Invalid ID for tutor",
"json-data-invalid": "Submitted data is no valid JSON",
"need-locationid": "No locationid given"
} \ No newline at end of file
diff --git a/modules-available/roomplanner/page.inc.php b/modules-available/roomplanner/page.inc.php
index 65025c5f..a35023b9 100644
--- a/modules-available/roomplanner/page.inc.php
+++ b/modules-available/roomplanner/page.inc.php
@@ -94,7 +94,8 @@ class Page_Roomplanner extends Page
. 'WHERE machineuuid LIKE :query '
. ' OR macaddr LIKE :query '
. ' OR clientip LIKE :query '
- . ' OR hostname LIKE :query ', ['query' => "%$query%"]);
+ . ' OR hostname LIKE :query '
+ . ' LIMIT 100', ['query' => "%$query%"]);
$returnObject = ['machines' => []];
@@ -191,14 +192,14 @@ class Page_Roomplanner extends Page
'gridCol' => $computer['gridCol'],
'itemlook' => $computer['itemlook']]);
- Database::exec('UPDATE machine SET position = :position, locationid = :locationid WHERE machineuuid = :muuid',
+ Database::exec('UPDATE machine SET position = :position, fixedlocationid = :locationid WHERE machineuuid = :muuid',
['locationid' => $this->locationid, 'muuid' => $computer['muuid'], 'position' => $position]);
}
$toDelete = array_diff($oldUuids, $newUuids);
foreach ($toDelete as $d) {
- Database::exec("UPDATE machine SET position = '', locationid = NULL WHERE machineuuid = :uuid", ['uuid' => $d]);
+ Database::exec("UPDATE machine SET position = '', fixedlocationid = NULL WHERE machineuuid = :uuid", ['uuid' => $d]);
}
}
@@ -228,13 +229,18 @@ class Page_Roomplanner extends Page
protected function getMachinesOnPlan($tutorUuid)
{
- $result = Database::simpleQuery('SELECT machineuuid, macaddr, clientip, hostname, position FROM machine WHERE locationid = :locationid',
+ $result = Database::simpleQuery('SELECT machineuuid, macaddr, clientip, hostname, position FROM machine WHERE fixedlocationid = :locationid',
['locationid' => $this->locationid]);
$machines = [];
while ($row = $result->fetch(PDO::FETCH_ASSOC)) {
$machine = [];
$pos = json_decode($row['position'], true);
- // TODO: Check if pos is valid (has required keys)
+ if ($pos === false || !isset($pos['gridRow']) || !isset($pos['gridCol'])) {
+ // Missing/incomplete position information - reset
+ Database::exec("UPDATE machine SET fixedlocationid = NULL, position = '' WHERE machineuuid = :uuid",
+ array('uuid' => $row['machineuuid']));
+ continue;
+ }
$machine['muuid'] = $row['machineuuid'];
$machine['ip'] = $row['clientip'];
@@ -255,14 +261,14 @@ class Page_Roomplanner extends Page
protected function getPotentialMachines()
{
- $result = Database::simpleQuery('SELECT machineuuid, macaddr, clientip, hostname '
- . 'FROM machine INNER JOIN subnet ON (INET_ATON(clientip) BETWEEN startaddr AND endaddr) '
- . 'WHERE subnet.locationid = :locationid', ['locationid' => $this->locationid]);
+ $result = Database::simpleQuery('SELECT m.machineuuid, m.macaddr, m.clientip, m.hostname, l.locationname AS otherroom
+ FROM machine m
+ LEFT JOIN location l ON (m.fixedlocationid = l.locationid AND m.subnetlocationid <> m.fixedlocationid)
+ WHERE subnetlocationid = :locationid', ['locationid' => $this->locationid]);
$machines = [];
while ($row = $result->fetch(PDO::FETCH_ASSOC)) {
- $row['combined'] = implode(' ', array_values($row));
$machines[] = $row;
}
diff --git a/modules-available/roomplanner/style.css b/modules-available/roomplanner/style.css
index 4ec8a7b0..1460364a 100644
--- a/modules-available/roomplanner/style.css
+++ b/modules-available/roomplanner/style.css
@@ -65,9 +65,6 @@ body {
right:-20px; }
-[itemlook="computer"] { background: url('images/computer/computer.png') no-repeat;}
-
-
[itemlook="door-nw"] { background: url('images/wall/door-nw.png') no-repeat;}
[itemlook="door-ne"] { background: url('images/wall/door-ne.png') no-repeat;}
[itemlook="door-sw"] { background: url('images/wall/door-sw.png') no-repeat;}
@@ -81,12 +78,12 @@ body {
background: url('images/electricalDevices_wIP/copier.png') no-repeat;
}
-/*
-[itemlook="pc"] {
- background: url('images/electricalDevices_wIP/pc.png') no-repeat;
- z-index:100;
+
+[itemlook|="pc"] {
+ border: 1px solid #999;
+ border-radius: 4px;
}
-*/
+
[itemlook="pc-east"] {
background: url('images/electricalDevices_wIP/pc_east.png') no-repeat;
}
@@ -745,10 +742,11 @@ div.draggable:hover .deleteHandle {
/* select popup */
.machine-entry {
width: 100%;
+ width: calc(100% - 5px);
border: 1px solid #999;
border-radius: 5px;
- margin: 5px 20px 5px 2px;
- padding: 5px 10px 5px 10px;
+ margin: 2px;
+ padding: 2px 4px;
}
/* in case it is already in the sketchboard */
@@ -756,34 +754,20 @@ div.draggable:hover .deleteHandle {
color: #666;
}
-.machine-entry .table {
- font-size:12px;
+.machine-entry table {
+ font-size: 12px;
margin-bottom: -5px;
+ width: 100%;
}
-.machine-entry .table tr {
- line-height: 5px;
+.machine-entry table tr {
+ border-top: 1px solid #bbb;
}
-
-.machine-logo {
- float: left;
- width:36px;
-}
-.machine-logo i {
- font-size: 20px;
- line-height: 50px;
-}
.machine-entry-header {
font-weight: bolder;
font-size: 18px;
}
-.machine-uuid {
-}
-
-.machine-ip {
-}
-
.selectize-dropdown-content {
max-height : 600px;
}
diff --git a/modules-available/roomplanner/templates/page.html b/modules-available/roomplanner/templates/page.html
index ba51b893..8bfa0ca4 100644
--- a/modules-available/roomplanner/templates/page.html
+++ b/modules-available/roomplanner/templates/page.html
@@ -330,7 +330,7 @@ var subnetMachines, roomConfiguration;
var plannerLoadState = 'invalid';
document.addEventListener("DOMContentLoaded", function () {
- subnetMachines = {{{subnetMachines}}};
+ subnetMachines = makeCombinedField({{{subnetMachines}}});
roomConfiguration = {{{roomConfiguration}}};
$.when(
diff --git a/modules-available/statistics/api.inc.php b/modules-available/statistics/api.inc.php
index 5fc57194..2ac6e782 100644
--- a/modules-available/statistics/api.inc.php
+++ b/modules-available/statistics/api.inc.php
@@ -32,7 +32,7 @@ if ($type{0} === '~') {
}
}
$NOW = time();
- $old = Database::queryFirst('SELECT logintime, lastseen FROM machine WHERE machineuuid = :uuid', array('uuid' => $uuid));
+ $old = Database::queryFirst('SELECT clientip, logintime, lastseen FROM machine WHERE machineuuid = :uuid', array('uuid' => $uuid));
if ($old !== false) {
settype($old['logintime'], 'integer');
settype($old['lastseen'], 'integer');
@@ -127,6 +127,12 @@ if ($type{0} === '~') {
'data' => $data,
'hostname' => $hostname,
));
+
+ if (($old === false || $old['clientip'] !== $ip) && Module::isAvailable('locations')) {
+ // New, or ip changed (dynamic pool?), update subnetlicationid
+ Location::updateMapIpToLocation($uuid, $ip);
+ }
+
// Write statistics data
} else if ($type === '~runstate') {
@@ -134,6 +140,10 @@ if ($type{0} === '~') {
$sessionLength = 0;
$used = Request::post('used', 0, 'integer');
if ($old === false) die("Unknown machine.\n");
+ if ($old['clientip'] !== $ip) {
+ EventLog::warning("[runstate] IP address of client $uuid seems to have changed ({$old['clientip']} -> $ip)");
+ die("Address changed.\n");
+ }
// Figure out what's happening
if ($used === 0) {
// Is not in use
@@ -173,12 +183,18 @@ if ($type{0} === '~') {
));
}
} elseif ($type === '~poweroff') {
- if ($old !== false && $old['logintime'] !== 0) {
+ if ($old === false) die("Unknown machine.\n");
+ if ($old['clientip'] !== $ip) {
+ EventLog::warning("[poweroff] IP address of client $uuid seems to have changed ({$old['clientip']} -> $ip)");
+ die("Address changed.\n");
+ }
+ if ($old['logintime'] !== 0) {
$sessionLength = $old['lastseen'] - $old['logintime'];
if ($sessionLength > 0 && $sessionLength < 86400*2) {
- Database::exec('INSERT INTO statistic (dateline, typeid, clientip, username, data)'
- . " VALUES (:start, '~session-length', :clientip, '', :length)", array(
+ Database::exec('INSERT INTO statistic (dateline, typeid, machineuuid, clientip, username, data)'
+ . " VALUES (:start, '~session-length', :uuid, :clientip, '', :length)", array(
'start' => $old['logintime'],
+ 'uuid' => $uuid,
'clientip' => $ip,
'length' => $sessionLength
));
diff --git a/modules-available/statistics/inc/filter.inc.php b/modules-available/statistics/inc/filter.inc.php
index 03dba6c3..6af6eed1 100644
--- a/modules-available/statistics/inc/filter.inc.php
+++ b/modules-available/statistics/inc/filter.inc.php
@@ -214,13 +214,11 @@ class LocationFilter extends Filter
{
settype($this->argument, 'int');
if ($this->argument === 0) {
- $joins[] = 'LEFT JOIN subnet s ON (INET_ATON(machine.clientip) BETWEEN s.startaddr AND s.endaddr)';
- return 'machine.locationid IS NULL AND s.locationid IS NULL';
+ $neg = $this->operator === '=' ? '' : 'NOT';
+ return "machine.locationid IS $neg NULL";
} else {
- $joins[] = ' LEFT JOIN subnet ON (INET_ATON(clientip) BETWEEN startaddr AND endaddr AND machine.locationid IS NULL) ';
$args['lid'] = $this->argument;
- $neg = $this->operator == '=' ? '' : 'NOT';
- return "$neg ((subnet.locationid = :lid) OR (machine.locationid = :lid))";
+ return "machine.locationid {$this->operator} :lid";
}
}
}
diff --git a/modules-available/statistics/install.inc.php b/modules-available/statistics/install.inc.php
index be07274c..0729d676 100644
--- a/modules-available/statistics/install.inc.php
+++ b/modules-available/statistics/install.inc.php
@@ -1,5 +1,8 @@
<?php
+// locationid trigger
+$addTrigger = false;
+
$res = array();
// The main statistic table used for log entries
@@ -23,7 +26,9 @@ $res[] = tableCreate('statistic', "
$res[] = tableCreate('machine', "
`machineuuid` char(36) CHARACTER SET ascii NOT NULL,
- `locationid` int(11) DEFAULT NULL,
+ `fixedlocationid` int(11) DEFAULT NULL COMMENT 'Manually set location (e.g. roomplanner)',
+ `subnetlocationid` int(11) DEFAULT NULL COMMENT 'Automatically determined location (e.g. from subnet match),
+ `locationid` int(11) DEFAULT NULL COMMENT 'Will be automatically set to fixedlocationid if not null, subnetlocationid otherwise',
`macaddr` char(17) CHARACTER SET ascii NOT NULL,
`clientip` varchar(45) CHARACTER SET ascii NOT NULL,
`firstseen` int(10) unsigned NOT NULL,
@@ -66,6 +71,10 @@ $res[] = tableCreate('pciid', "
PRIMARY KEY (`category`,`id`)
");
+if (in_array(UPDATE_DONE, $res)) {
+ $addTrigger = true;
+}
+
//
// This was added/changed later -- keep update path
//
@@ -112,6 +121,42 @@ if ($ret === false) {
finalResponse(UPDATE_FAILED, 'Expanding position column failed: ' . Database::lastError());
}
+// 2016-12-06:
+// Add subnetlocationid - contains automatically determined location (by subnet)
+if (!tableHasColumn('machine', 'subnetlocationid')) {
+ $ret = Database::exec('ALTER TABLE machine'
+ . ' ADD COLUMN `subnetlocationid` int(11) DEFAULT NULL AFTER `machineuuid`');
+ if ($ret === false) {
+ finalResponse(UPDATE_FAILED, 'Adding subnetlocationid to machine failed: ' . Database::lastError());
+ }
+ $res[] = UPDATE_DONE;
+ $addTrigger = true;
+}
+// And fixedlocationid - manually set location, currently used by roomplanner
+if (!tableHasColumn('machine', 'fixedlocationid')) {
+ $ret = Database::exec('ALTER TABLE machine'
+ . ' ADD COLUMN `fixedlocationid` int(11) DEFAULT NULL AFTER `machineuuid`');
+ if ($ret === false) {
+ finalResponse(UPDATE_FAILED, 'Adding fixedlocationid to machine failed: ' . Database::lastError());
+ }
+ // Now copy over the values from locationid, since this was used before
+ Database::exec("UPDATE machine SET fixedlocationid = locationid");
+ $res[] = UPDATE_DONE;
+ $addTrigger = true;
+}
+// If any of these was added, create the trigger
+if ($addTrigger) {
+ $ret = Database::exec("
+ CREATE TRIGGER set_automatic_locationid
+ BEFORE UPDATE ON machine FOR EACH ROW
+ BEGIN
+ SET NEW.locationid = If(NEW.fixedlocationid IS NULL, NEW.subnetlocationid, NEW.fixedlocationid);
+ END");
+ if ($ret === false) {
+ finalResponse(UPDATE_FAILED, 'Adding locationid trigger to machine failed: ' . Database::lastError());
+ }
+}
+
// Create response
if (in_array(UPDATE_DONE, $res)) {
diff --git a/modules-available/statistics/page.inc.php b/modules-available/statistics/page.inc.php
index ab7d589c..6a9acd14 100644
--- a/modules-available/statistics/page.inc.php
+++ b/modules-available/statistics/page.inc.php
@@ -224,7 +224,7 @@ class Page_Statistics extends Page
}
- private function capChart(&$json, $cutoff, $minSlice = 0.015)
+ private function capChart(&$json, &$rows, $cutoff, $minSlice = 0.015)
{
$total = 0;
foreach ($json as $entry) {
@@ -243,6 +243,9 @@ class Page_Statistics extends Page
++$id;
$accounted += $entry['value'];
}
+ for ($i = $id; $i < count($rows); ++$i) {
+ $rows[$i]['collapse'] = 'collapse';
+ }
$json = array_slice($json, 0, $id);
if ($accounted / $total < 0.99) {
$json[] = array(
@@ -333,7 +336,7 @@ class Page_Statistics extends Page
);
++$id;
}
- $this->capChart($json, 0.92);
+ $this->capChart($json, $lines, 0.92);
Render::addTemplate('cpumodels', array('rows' => $lines, 'query' => $this->query, 'json' => json_encode($json)));
}
@@ -379,7 +382,7 @@ class Page_Statistics extends Page
);
++$id;
}
- $this->capChart($json, 0.92);
+ $this->capChart($json, $data['rows'], 0.92);
$data['json'] = json_encode($json);
$data['query'] = $this->query;
Render::addTemplate('memory', $data);
@@ -455,7 +458,7 @@ class Page_Statistics extends Page
'value' => $v,
);
}
- $this->capChart($json, 0.95);
+ $this->capChart($json, $data['rows'], 0.95);
$data['json'] = json_encode($json);
$data['query'] = $this->query;
Render::addTemplate('id44', $data);
@@ -468,7 +471,7 @@ class Page_Statistics extends Page
{
$filterSet->makeFragments($where, $join, $sort, $args);
- $args['cutoff'] = ceil(time() / 3600) * 3600 - 86400 * 7;
+ $args['cutoff'] = ceil(time() / 3600) * 3600 - 86400 * 10;
$res = Database::simpleQuery("SELECT machineuuid, clientip, hostname, firstseen, mbram, kvmstate, id44mb FROM machine $join"
. " WHERE firstseen > :cutoff AND $where ORDER BY firstseen DESC LIMIT 32", $args);
@@ -486,7 +489,7 @@ class Page_Statistics extends Page
$row['hddclass'] = $this->hddColorClass($row['gbtmp']);
$row['kvmicon'] = $row['kvmstate'] === 'ENABLED' ? '✓' : '✗';
if (++$count > 5) {
- $row['style'] = 'display:none';
+ $row['collapse'] = 'collapse';
}
$rows[] = $row;
}
@@ -535,7 +538,7 @@ class Page_Statistics extends Page
$row['hostname'] = $row['clientip'];
}
if (isset($row['data'])) {
- if (!preg_match('/^Disk.*bytes$/m', $row['data'])) {
+ if (!preg_match('/^(Disk.* bytes|Disk.*\d{5,} sectors)/m', $row['data'])) {
$row['nohdd'] = true;
}
}
@@ -711,9 +714,6 @@ class Page_Statistics extends Page
unset($client['data']);
// Get locations
if (Module::isAvailable('locations')) {
- if (is_null($client['locationid'])) {
- $client['locationid'] = Location::getFromIp($client['clientip']);
- }
$locs = Location::getLocationsAssoc();
$next = (int)$client['locationid'];
$output = array();
@@ -804,33 +804,35 @@ class Page_Statistics extends Page
Render::addTemplate('machine-hdds', $hdds);
}
// Client log
- $lres = Database::simpleQuery('SELECT logid, dateline, logtypeid, clientip, description, extra FROM clientlog'
- . ' WHERE clientip = :clientip ORDER BY logid DESC LIMIT 25', array('clientip' => $client['clientip']));
- $today = date('d.m.Y');
- $yesterday = date('d.m.Y', time() - 86400);
- $count = 0;
- $log = array();
- while ($row = $lres->fetch(PDO::FETCH_ASSOC)) {
- if (substr($row['description'], -5) === 'on :0' && strpos($row['description'], 'root logged') === false) {
- continue;
- }
- $day = date('d.m.Y', $row['dateline']);
- if ($day === $today) {
- $day = Dictionary::translate('lang_today');
- } elseif ($day === $yesterday) {
- $day = Dictionary::translate('lang_yesterday');
- }
- $row['date'] = $day . date(' H:i', $row['dateline']);
- $row['icon'] = $this->eventToIconName($row['logtypeid']);
- $log[] = $row;
- if (++$count === 10) {
- break;
+ if (Module::get('syslog') !== false) {
+ $lres = Database::simpleQuery('SELECT logid, dateline, logtypeid, clientip, description, extra FROM clientlog'
+ . ' WHERE clientip = :clientip ORDER BY logid DESC LIMIT 25', array('clientip' => $client['clientip']));
+ $today = date('d.m.Y');
+ $yesterday = date('d.m.Y', time() - 86400);
+ $count = 0;
+ $log = array();
+ while ($row = $lres->fetch(PDO::FETCH_ASSOC)) {
+ if (substr($row['description'], -5) === 'on :0' && strpos($row['description'], 'root logged') === false) {
+ continue;
+ }
+ $day = date('d.m.Y', $row['dateline']);
+ if ($day === $today) {
+ $day = Dictionary::translate('lang_today');
+ } elseif ($day === $yesterday) {
+ $day = Dictionary::translate('lang_yesterday');
+ }
+ $row['date'] = $day . date(' H:i', $row['dateline']);
+ $row['icon'] = $this->eventToIconName($row['logtypeid']);
+ $log[] = $row;
+ if (++$count === 10) {
+ break;
+ }
}
+ Render::addTemplate('syslog', array(
+ 'clientip' => $client['clientip'],
+ 'list' => $log,
+ ));
}
- Render::addTemplate('syslog', array(
- 'clientip' => $client['clientip'],
- 'list' => $log,
- ));
// Notes
Render::addTemplate('machine-notes', $client);
}
diff --git a/modules-available/statistics/templates/cpumodels.html b/modules-available/statistics/templates/cpumodels.html
index 20da4697..d9b0298b 100644
--- a/modules-available/statistics/templates/cpumodels.html
+++ b/modules-available/statistics/templates/cpumodels.html
@@ -13,7 +13,7 @@
<th class="text-right text-nowrap">{{lang_modelCount}}</th>
</tr>
{{#rows}}
- <tr id="{{id}}">
+ <tr id="{{id}}" class="{{collapse}}">
<td class="text-left text-nowrap filter-col" data-filter-col="systemmodel">
<table style="width:100%; table-layout: fixed;"><tr><td style="overflow:hidden;text-overflow: ellipsis;">
<a class="filter-val" data-filter-val="{{systemmodel}}" href="?do=Statistics&amp;show=stat&amp;filters={{query}}~,~systemmodel={{urlsystemmodel}}">{{systemmodel}}</a>
@@ -25,6 +25,15 @@
<td class="text-right">{{count}}</td>
</tr>
{{/rows}}
+ <tr class="slx-decollapse">
+ <td colspan="3">
+ <span class="btn-group btn-group-justified">
+ <span class="btn btn-default btn-sm">
+ <span class="glyphicon glyphicon-menu-down"></span>
+ </span>
+ </span>
+ </td>
+ </tr>
</table>
</div>
<div class="col-md-4">
diff --git a/modules-available/statistics/templates/id44.html b/modules-available/statistics/templates/id44.html
index 84b515d1..38cf028f 100644
--- a/modules-available/statistics/templates/id44.html
+++ b/modules-available/statistics/templates/id44.html
@@ -12,13 +12,22 @@
<th class="text-right">{{lang_machineCount}}</th>
</tr>
{{#rows}}
- <tr id="tmpid{{gb}}" class="{{class}}">
+ <tr id="tmpid{{gb}}" class="{{class}} {{collapse}}">
<td class="text-left text-nowrap">
<a class="filter-val" data-filter-val="{{gb}}" href="?do=Statistics&amp;show=stat&amp;filters={{query}}~,~hddgb={{gb}}">{{gb}}&thinsp;GiB</a>
</td>
<td class="text-right">{{count}}</td>
</tr>
{{/rows}}
+ <tr class="slx-decollapse">
+ <td colspan="2">
+ <span class="btn-group btn-group-justified">
+ <span class="btn btn-default btn-sm">
+ <span class="glyphicon glyphicon-menu-down"></span>
+ </span>
+ </span>
+ </td>
+ </tr>
</table>
</div>
<div class="col-sm-6">
diff --git a/modules-available/statistics/templates/memory.html b/modules-available/statistics/templates/memory.html
index ae040674..f17f55ca 100644
--- a/modules-available/statistics/templates/memory.html
+++ b/modules-available/statistics/templates/memory.html
@@ -12,13 +12,22 @@
<th class="text-right">{{lang_machineCount}}</th>
</tr>
{{#rows}}
- <tr id="ramid{{gb}}" class="{{class}}">
+ <tr id="ramid{{gb}}" class="{{class}} {{collapse}}">
<td class="text-left text-nowrap">
<a class="filter-val" data-filter-val="{{gb}}" href="?do=Statistics&amp;show=stat&amp;filters={{query}}~,~gbram={{gb}}">{{gb}}&thinsp;GiB</a>
</td>
<td class="text-right">{{count}}</td>
</tr>
{{/rows}}
+ <tr class="slx-decollapse">
+ <td colspan="2">
+ <span class="btn-group btn-group-justified">
+ <span class="btn btn-default btn-sm">
+ <span class="glyphicon glyphicon-menu-down"></span>
+ </span>
+ </span>
+ </td>
+ </tr>
</table>
</div>
<div class="col-sm-6">
diff --git a/modules-available/statistics/templates/newclients.html b/modules-available/statistics/templates/newclients.html
index e7d79818..c5c704d1 100644
--- a/modules-available/statistics/templates/newclients.html
+++ b/modules-available/statistics/templates/newclients.html
@@ -4,7 +4,7 @@
{{lang_newMachines}}
</div>
<div class="panel-body">
- <table class="table table-condensed table-striped" id="newclienttable">
+ <table class="table table-condensed table-striped">
<tr>
<th>{{lang_machine}}</th>
<th class="text-right"></th>
@@ -13,7 +13,7 @@
<th class="text-right">HDD</th>
</tr>
{{#rows}}
- <tr style="{{style}}">
+ <tr class="{{collapse}}">
<td class="text-nowrap"><a href="?do=Statistics&amp;uuid={{machineuuid}}">{{hostname}}</a></td>
<td class="text-right">{{firstseen}}</td>
<td class="{{kvmclass}}">{{kvmicon}}</td>
@@ -21,23 +21,15 @@
<td class="text-right {{hddclass}}">{{gbtmp}}&thinsp;GiB</td>
</tr>
{{/rows}}
- {{#openbutton}}
- <tr>
- <td colspan="5" onclick="slxExpandNew(this)">
+ <tr class="slx-decollapse">
+ <td colspan="5">
<span class="btn-group btn-group-justified">
- <span class="btn btn-default">
+ <span class="btn btn-default btn-sm">
<span class="glyphicon glyphicon-menu-down"></span>
</span>
</span>
- <script type="text/javascript">
- function slxExpandNew(b) {
- $('#newclienttable').find('tr').show();
- $(b).hide();
- }
- </script>
</td>
</tr>
- {{/openbutton}}
</table>
</div>
</div>
diff --git a/modules-available/sysconfig/addmodule_adauth.inc.php b/modules-available/sysconfig/addmodule_adauth.inc.php
index 238b7490..666c36d1 100644
--- a/modules-available/sysconfig/addmodule_adauth.inc.php
+++ b/modules-available/sysconfig/addmodule_adauth.inc.php
@@ -25,7 +25,7 @@ class AdAuth_Start extends AddModule_Base
if (!empty($obdn)) {
$data['binddn'] = $obdn;
}
- if (preg_match('/^(.*)\:(636|3269|389|3268)$/', $data['server'], $out)) {
+ if (isset($data['server']) && preg_match('/^(.*)\:(636|3269|389|3268)$/', $data['server'], $out)) {
$data['server'] = $out[1];
}
$data['step'] = 'AdAuth_CheckConnection';
@@ -146,13 +146,11 @@ class AdAuth_SelfSearch extends AddModule_Base
'searchbase' => $searchbase,
'bindpw' => $bindpw,
);
- error_log("'$binddn'");
- error_log(preg_match(AD_SHORT_REGEX, $binddn, $out));
- error_log(print_r($out, true));
if (preg_match(AD_SHORT_REGEX, $binddn, $out) && !empty($out[2])) {
$this->originalBindDn = str_replace('/', '\\', $binddn);
$taskData['filter'] = 'sAMAccountName=' . $out[2];
} elseif (preg_match(AD_AT_REGEX, $binddn, $out) && !empty($out[1])) {
+ $this->originalBindDn = $binddn;
$taskData['filter'] = 'sAMAccountName=' . $out[1];
} elseif (preg_match('/^cn\=([^\=]+),.*?,dc\=([^\=]+),/i', Ldap::normalizeDn($binddn), $out)) {
if (empty($searchbase)) {
@@ -220,7 +218,7 @@ class AdAuth_HomeAttrCheck extends AddModule_Base
$bindpw = Request::post('bindpw');
$ssl = Request::post('ssl', 'off') === 'on';
if ($ssl && !Request::post('fingerprint')) {
- Message::addError('error-read', 'fingerprint');
+ Message::addError('main.error-read', 'fingerprint');
AddModule_Base::setStep('AdAuth_Start'); // Continues with AdAuth_Start for render()
return;
}
@@ -290,7 +288,7 @@ class AdAuth_CheckCredentials extends AddModule_Base
$bindpw = Request::post('bindpw');
$ssl = Request::post('ssl', 'off') === 'on';
if ($ssl && !Request::post('fingerprint')) {
- Message::addError('error-read', 'fingerprint');
+ Message::addError('main.error-read', 'fingerprint');
AddModule_Base::setStep('AdAuth_Start'); // Continues with AdAuth_Start for render()
return;
}
diff --git a/modules-available/sysconfig/addmodule_ldapauth.inc.php b/modules-available/sysconfig/addmodule_ldapauth.inc.php
index 4a204407..2bd4b584 100644
--- a/modules-available/sysconfig/addmodule_ldapauth.inc.php
+++ b/modules-available/sysconfig/addmodule_ldapauth.inc.php
@@ -46,9 +46,9 @@ class LdapAuth_CheckConnection extends AddModule_Base
$ports = array($out[2]);
$this->server = $out[1];
} elseif ($ssl) {
- $ports = array(636, 3269);
+ $ports = array(636);
} else {
- $ports = array(389, 3268);
+ $ports = array(389);
}
$this->scanTask = Taskmanager::submit('PortScan', array(
'host' => $this->server,
@@ -196,7 +196,8 @@ class LdapAuth_HomeDir extends AddModule_Base
$data['shareRemapMode_' . $this->edit->getData('shareRemapMode')] = 'selected="selected"';
$letter = $this->edit->getData('shareHomeDrive');
} else {
- $data['shareDownloads'] = $data['shareMedia'] = $data['shareDocuments'] = 'selected="selected"';
+ $data['shareDownloads_c'] = $data['shareMedia_c'] = $data['shareDocuments_c'] = $data['shareRemapCreate_c'] = 'checked="checked"';
+ $data['shareRemapMode_1'] = 'selected="selected"';
$letter = 'H:';
}
$data['drives'] = array();
diff --git a/modules-available/sysconfig/lang/de/messages.json b/modules-available/sysconfig/lang/de/messages.json
index 15af1c33..0a1f6de3 100644
--- a/modules-available/sysconfig/lang/de/messages.json
+++ b/modules-available/sysconfig/lang/de/messages.json
@@ -2,6 +2,7 @@
"config-activated": "Konfiguration {{0}} wurde aktiviert",
"config-deleted": "Konfiguration {{0}} wurde gel\u00f6scht",
"config-invalid": "Konfiguration mit ID {{0}} existiert nicht",
+ "could-not-determine-binddn": "Konnte Bind-DN nicht ermitteln",
"invalid-action": "Ung\u00fcltige Aktion: {{0}}",
"missing-file": "Es wurde keine Datei ausgew\u00e4hlt!",
"missing-title": "Kein Titel eingegeben",
diff --git a/modules-available/sysconfig/lang/de/template-tags.json b/modules-available/sysconfig/lang/de/template-tags.json
index 3944a91c..b2d5dfd0 100644
--- a/modules-available/sysconfig/lang/de/template-tags.json
+++ b/modules-available/sysconfig/lang/de/template-tags.json
@@ -32,6 +32,7 @@
"lang_customModuleInfo1": "\u00dcber ein benutzerdefiniertes Modul ist es m\u00f6glich, beliebige Dateien zum Linux-Grundsystem, das auf den Clients gebootet wird, hinzuzuf\u00fcgen. Dazu kann ein Archiv mit einer Dateisystemstruktur hochgeladen werden, die in dieser Form 1:1 in das gebootete Linux extrahiert wird.",
"lang_customModuleInfo2": "Beispiel: Enth\u00e4lt das hochgeladene Archiv eine Datei etc\/beispiel.conf, so wird auf einem gebooteten Client diese Datei als \/etc\/beispiel.conf zu finden sein.",
"lang_deleteLong": "Modul oder Konfiguration l\u00f6schen.",
+ "lang_determiningHomeDirectory": "Versuche Attribut f\u00fcr das Home-Verzeichnis zu ermitteln...",
"lang_dnLookup": "Ermitteln der Bind-DN",
"lang_download": "Herunterladen",
"lang_downloadLong": "Dieses Modul \"so wie es ist\" herunterladen.",
@@ -66,6 +67,7 @@
"lang_newConfiguration": "Neue Konfiguration",
"lang_newModule": "Neues Modul",
"lang_noContent": "Kein Inhalt!",
+ "lang_noHomeAttrFound": "Konnte kein Attribut finden, dass das Home-Verzeichnis zu enthalten scheint. Bitte manuell festlegen, wenn Einbindung gew\u00fcnscht.",
"lang_noModuleOfType": "Kein Modul dieser Art vorhanden.",
"lang_noOpenPort": "Auf dem angegebenen Server wurde kein offener Port gefunden.",
"lang_noValidCert": "Der Server besitzt kein oder ein nicht valides Zertifikat.",
@@ -103,8 +105,10 @@
"lang_title": "Titel",
"lang_to": "Zur",
"lang_toSystemConfiguration": "Zur Systemkonfiguration",
+ "lang_tryingFingerprint": "Das Zertifikat des Servers kann nicht validiert werden. Wenn Sie fortfahren, wird in Zukunft der Fingerprint des Zertifikats f\u00fcr die Validierung genutzt. Wenn sich das Zertifikat \u00e4ndert, m\u00fcssen Sie diesen Wizard erneut durchf\u00fchren, um den Fingerprint zu aktualisieren. Wichtig: Wenn Sie einen Load-Balancer einsetzen, und die dahintergeschalteten Server unterschiedliche Zertifikate besitzen, k\u00f6nnen Sie dieses Verfahren nicht nutzen.",
"lang_upload": "Hochladen",
"lang_urlLoad": "Bild von URL laden",
+ "lang_userCertInvalid": "Das von Ihnen angegebene Zertifikat kann nicht zur Verifikation des Servers genutzt werden. Bitte geben Sie das passende Zertifikat an, oder lassen Sie das Feld leer, damit der Wizard versucht, das Zertifikat automatisch zu ermitteln.",
"lang_userDirectory": "Benutzerverzeichnis",
"lang_userDirectoryInfo1": "Optionale Angabe: Wenn die Clients f\u00fcr die Benutzer ein eigenes Verzeichnis (Homeverzeichnis, Benutzerverzeichnis) von einem Server einbinden sollen, geben Sie bitte hier das Format in UNC-Notation an, also z.B.",
"lang_userDirectoryInfo2": "%s ist dabei ein Platzhalter f\u00fcr den Login-Namen des Benutzers.",
diff --git a/modules-available/sysconfig/lang/en/messages.json b/modules-available/sysconfig/lang/en/messages.json
index 888b2e6b..83f47903 100644
--- a/modules-available/sysconfig/lang/en/messages.json
+++ b/modules-available/sysconfig/lang/en/messages.json
@@ -2,6 +2,7 @@
"config-activated": "Configuration {{0}} has been activated",
"config-deleted": "Deleted configuration {{0}}",
"config-invalid": "Configuration with id {{0}} does not exist",
+ "could-not-determine-binddn": "Could not determine bind dn",
"invalid-action": "Invalid action: {{0}}",
"missing-file": "There was no file selected!",
"missing-title": "No title given",
diff --git a/modules-available/sysconfig/lang/en/module.json b/modules-available/sysconfig/lang/en/module.json
index fe4f346e..526f6562 100644
--- a/modules-available/sysconfig/lang/en/module.json
+++ b/modules-available/sysconfig/lang/en/module.json
@@ -1,10 +1,10 @@
{
"lang_clientSshConfig": "SSH configuration",
"lang_configurationCompilation": "Compile configuration",
- "lang_contentOf": "Content of \"",
+ "lang_contentOf": "Content of",
"lang_moduleAdd": "Add Module",
"lang_noModuleFromThisGroup": "(No module from this group)",
"lang_unknwonTaskManager": "Unknown Task Manager error",
"module_name": "Localization",
"page_title": "Localize and integrate"
-} \ No newline at end of file
+}
diff --git a/modules-available/sysconfig/lang/en/template-tags.json b/modules-available/sysconfig/lang/en/template-tags.json
index 512a787d..15516bf2 100644
--- a/modules-available/sysconfig/lang/en/template-tags.json
+++ b/modules-available/sysconfig/lang/en/template-tags.json
@@ -32,6 +32,7 @@
"lang_customModuleInfo1": "About a custom module, it is possible to add arbitrary files to a Linux system that is booted clients. For this purpose, an archive can be uploaded using a file system structure that is extracted in this form 1:1 in the booted Linux.",
"lang_customModuleInfo2": "Example: If the uploaded archive is the file etc\/example.conf, this file will be located as \/etc\/example.conf to a booted client.",
"lang_deleteLong": "Delete module or configuration.",
+ "lang_determiningHomeDirectory": "Trying to determine home directory attribute...",
"lang_dnLookup": "Looking up bind dn",
"lang_download": "Download",
"lang_downloadLong": "Download module \"as is\".",
@@ -66,6 +67,7 @@
"lang_newConfiguration": "New Configuration",
"lang_newModule": "New Module",
"lang_noContent": "No content!",
+ "lang_noHomeAttrFound": "No home directory attribute found. Please specify it manually if you need home directories.",
"lang_noModuleOfType": "No module of this type found.",
"lang_noOpenPort": "There is no open LDAP port on this server.",
"lang_noValidCert": "The server did not supply a certificate, or the certificate is invalid.",
@@ -103,8 +105,10 @@
"lang_title": "Title",
"lang_to": "To",
"lang_toSystemConfiguration": "Go to system configuration",
+ "lang_tryingFingerprint": "Server does not seem to have a valid certificate. If you continue, its fingerprint will be used for verification. This means you have to re-run this wizard whenever the server's certificate changes. Also note that this method will not work if you're using a load balancer and the servers behind it have individual certificates.",
"lang_upload": "Upload",
"lang_urlLoad": "Load image from URL",
+ "lang_userCertInvalid": "The certificate you specified could not be used to verify the server. Please make sure you pass the appropriate certificate to this wizard, or leave the field blank to let the wizard try and determine the proper certificate.",
"lang_userDirectory": "User Directory",
"lang_userDirectoryInfo1": "Optional: If the clients should embed a separate directory (home directory, user directory) from a server for the user, please enter here the format in UNC notation, eg",
"lang_userDirectoryInfo2": "%s is a placeholder for the user's login name.",
diff --git a/modules-available/sysconfig/templates/ad-selfsearch.html b/modules-available/sysconfig/templates/ad-selfsearch.html
index b60cc3af..39ef3e59 100644
--- a/modules-available/sysconfig/templates/ad-selfsearch.html
+++ b/modules-available/sysconfig/templates/ad-selfsearch.html
@@ -1,5 +1,10 @@
<p>
+ {{#tryHomeAttr}}
+ {{lang_determiningHomeDirectory}}
+ {{/tryHomeAttr}}
+ {{^tryHomeAttr}}
{{lang_dnLookup}}
+ {{/tryHomeAttr}}
</p>
<p>
{{binddn}} @ {{server}}<br>
@@ -14,7 +19,11 @@
</div>
</div>
<i>{{lang_onProblemSearchBase}}</i>
-<br><br>
+<br>
+<div class="alert alert-warning" id="nohome" style="display:none">
+ {{lang_noHomeAttrFound}}
+</div>
+<br>
<div class="pull-left">
<form role="form" method="post" action="?do=SysConfig&amp;action=addmodule&amp;step={{prev}}">
<input type="hidden" name="token" value="{{token}}">
@@ -87,9 +96,11 @@
tryHomeAttr = true;
if (task.data.home && task.data.home.length) attrlist = task.data.home;
{{/tryHomeAttr}}
- if (typeof search !== 'string' || search.length === 0 || search.length + 2 >= fulldn.length
- || (tryHomeAttr && $('#home').val().length === 0 && $('#homeattr').val().length === 0 && attrlist.length === 0)) {
+ if (typeof search !== 'string' || search.length === 0 || search.length + 2 >= fulldn.length) {
+ $('#nextbutton').html('{{lang_continueAnyway}}');
+ } else if (tryHomeAttr && $('#home').val().length === 0 && $('#homeattr').val().length === 0 && attrlist.length === 0) {
$('#nextbutton').html('{{lang_continueAnyway}}');
+ $('#nohome').show();
} else if (attrlist.length > 1 && $('#homeattr').val().length === 0) {
var sel = $('<select>').attr('onchange', 'slxSetHomeAttr(this)').addClass('form-control');
var best = 0;
diff --git a/modules-available/sysconfig/templates/ad_ldap-checkconnection.html b/modules-available/sysconfig/templates/ad_ldap-checkconnection.html
index 5925829a..0ee596ab 100644
--- a/modules-available/sysconfig/templates/ad_ldap-checkconnection.html
+++ b/modules-available/sysconfig/templates/ad_ldap-checkconnection.html
@@ -8,6 +8,8 @@
<div id="self-signed" style="display:none" class="alert alert-info">{{lang_selfSignedNote}}</div>
<div id="no-valid-cert" style="display:none" class="alert alert-danger">{{lang_noValidCert}}</div>
<div id="no-open-port" style="display:none" class="alert alert-danger">{{lang_noOpenPort}}</div>
+<div id="supplied-cert-invalid" style="display:none" class="alert alert-danger">{{lang_userCertInvalid}}</div>
+<div id="trying-fingerprint" style="display:none" class="alert alert-warning">{{lang_tryingFingerprint}}</div>
<br>
<div class="pull-left">
<form role="form" method="post" action="?do=SysConfig&amp;action=addmodule&amp;step={{prev}}">
@@ -52,7 +54,15 @@
<script type="text/javascript">
function isSelfSigned(code)
{
- return code == 18 || code == 19 || code == 20 || code == 21;
+ return code == 19;
+ }
+ function isIncomplete(code)
+ {
+ return code == 18 || code == 20 || code == 21;
+ }
+ function isValid(code)
+ {
+ return code == 0;
}
function portScan(task)
{
@@ -62,27 +72,44 @@
var ssl = $('#ssl').length > 0;
var ports = task.data.ports;
var verRes = -1;
- var cert = ssl && $('#certificate').val().length > 10;
+ var userCert = ssl && $('#certificate').val().length > 10;
+ var openPort = false;
for (var i = 0; i < ports.length; ++i) {
if (!ports[i].open || !ports[i].port) continue;
if ($.isNumeric($('#port').val()) && $('#port').val() < ports[i].port) continue; // Prefer the global LDAP ports over the specific AD ports
+ openPort = true;
if (ssl) {
if (verRes === -1) verRes = ports[i].verifyResult;
+ if (typeof ports[i].certFingerprint !== 'string' || typeof ports[i].certificateChain !== 'string') continue;
if (ports[i].certFingerprint.length < 10 || ports[i].certificateChain.length < 10) continue;
- if (ports[i].verifyResult != 0 && (cert || !isSelfSigned(ports[i].verifyResult))) continue;
+ if (!isValid(ports[i].verifyResult) && userCert) continue;
+ if (!isValid(ports[i].verifyResult) && !isSelfSigned(ports[i].verifyResult) && !isIncomplete(ports[i].verifyResult)) continue;
verRes = ports[i].verifyResult;
$('#fingerprint').val(ports[i].certFingerprint);
- if (!cert && verRes != 0) $('#certificate').val(ports[i].certificateChain);
- else if (!cert && verRes == 0) $('#certificate').val('default');
+ if (!userCert && isSelfSigned(verRes)) {
+ $('#certificate').val(ports[i].certificateChain);
+ } else if (!userCert && isValid(verRes)) {
+ $('#certificate').val('default');
+ } else if (!userCert) {
+ $('#certificate').val('');
+ }
}
$('#port').val(ports[i].port);
}
- if (ssl && verRes != 0 && (cert || !isSelfSigned(verRes))) {
- $('#no-valid-cert').css('display', '');
- } else if ($('#port').val() > 0) {
+ if (openPort && ssl && !isValid(verRes)) {
+ if (userCert) {
+ $('#supplied-cert-invalid').show();
+ } else if (isSelfSigned(verRes)) {
+ $('#self-signed').show();
+ } else if (isIncomplete(verRes)) {
+ $('#trying-fingerprint').show();
+ } else {
+ $('#no-valid-cert').show();
+ }
+ }
+ if (openPort) {
$('#nextbutton').show();
- if (ssl && isSelfSigned(verRes)) $('#self-signed').css('display', '');
- else $('#nextform').submit();
+ if (!ssl || isValid(verRes)) $('#nextform').submit();
} else {
$('#no-open-port').css('display', '');
}
diff --git a/modules-available/sysconfig/templates/list-configs.html b/modules-available/sysconfig/templates/list-configs.html
index 99db96bd..d2cedca4 100644
--- a/modules-available/sysconfig/templates/list-configs.html
+++ b/modules-available/sysconfig/templates/list-configs.html
@@ -18,7 +18,7 @@
<input type="hidden" name="token" value="{{token}}">
<input type="hidden" name="action" value="config">
<input type="hidden" name="locationid" value="{{locationid}}">
- <table id="conftable" class="slx-table" style="width:100%">
+ <table id="conftable" class="slx-table table-hover" style="width:100%">
{{#configs}}
<tr>
<td data-modlist="{{modlist}}" class="slx-pointer" onclick="showmod(this, 'bold')" onmouseover="showmod(this, 'fade')" onmouseout="showmod(this, 'reset')" width="100%">
diff --git a/modules-available/sysconfig/templates/list-modules.html b/modules-available/sysconfig/templates/list-modules.html
index e7a3b26c..c6622ee7 100644
--- a/modules-available/sysconfig/templates/list-modules.html
+++ b/modules-available/sysconfig/templates/list-modules.html
@@ -8,7 +8,7 @@
<form method="post" action="?do=SysConfig">
<input type="hidden" name="token" value="{{token}}">
<input type="hidden" name="action" value="module">
- <table id="modtable" class="slx-table" style="width:100%">
+ <table id="modtable" class="slx-table table-hover" style="width:100%">
{{#modules}}
<tr>
<td class="badge text-nowrap">{{moduleType}}</td>
diff --git a/modules-available/webinterface/lang/en/template-tags.json b/modules-available/webinterface/lang/en/template-tags.json
index 31bc1cc9..4d91e4b6 100644
--- a/modules-available/webinterface/lang/en/template-tags.json
+++ b/modules-available/webinterface/lang/en/template-tags.json
@@ -12,6 +12,6 @@
"lang_passwordFields": "Password fields",
"lang_passwordsDescription": "Set whether password fields should be masked or not. The password field of the login page to the web interface is always masked.",
"lang_privateKey": "Please paste the private key belonging to the certificate here. It has to be in \"pem\" format too, which should look like this:",
- "lang_randomCert": "Genenrate new self-signed certificate",
+ "lang_randomCert": "Generate new self-signed certificate",
"lang_showPasswords": "Show passwords"
-} \ No newline at end of file
+}