diff options
Diffstat (limited to 'app/assets/javascripts/gl_dropdown.js')
-rw-r--r-- | app/assets/javascripts/gl_dropdown.js | 705 |
1 files changed, 705 insertions, 0 deletions
diff --git a/app/assets/javascripts/gl_dropdown.js b/app/assets/javascripts/gl_dropdown.js new file mode 100644 index 00000000000..c5d92831fbe --- /dev/null +++ b/app/assets/javascripts/gl_dropdown.js @@ -0,0 +1,705 @@ +(function() { + var GitLabDropdown, GitLabDropdownFilter, GitLabDropdownRemote, + bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, + indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; + + GitLabDropdownFilter = (function() { + var ARROW_KEY_CODES, BLUR_KEYCODES, HAS_VALUE_CLASS; + + BLUR_KEYCODES = [27, 40]; + + ARROW_KEY_CODES = [38, 40]; + + HAS_VALUE_CLASS = "has-value"; + + function GitLabDropdownFilter(input, options) { + var $clearButton, $inputContainer, ref, timeout; + this.input = input; + this.options = options; + this.filterInputBlur = (ref = this.options.filterInputBlur) != null ? ref : true; + $inputContainer = this.input.parent(); + $clearButton = $inputContainer.find('.js-dropdown-input-clear'); + this.indeterminateIds = []; + $clearButton.on('click', (function(_this) { + return function(e) { + e.preventDefault(); + e.stopPropagation(); + return _this.input.val('').trigger('keyup').focus(); + }; + })(this)); + timeout = ""; + this.input.on("keyup", (function(_this) { + return function(e) { + var keyCode; + keyCode = e.which; + if (ARROW_KEY_CODES.indexOf(keyCode) >= 0) { + return; + } + if (_this.input.val() !== "" && !$inputContainer.hasClass(HAS_VALUE_CLASS)) { + $inputContainer.addClass(HAS_VALUE_CLASS); + } else if (_this.input.val() === "" && $inputContainer.hasClass(HAS_VALUE_CLASS)) { + $inputContainer.removeClass(HAS_VALUE_CLASS); + } + if (keyCode === 13) { + return false; + } + if (_this.options.remote) { + clearTimeout(timeout); + return timeout = setTimeout(function() { + var blur_field; + blur_field = _this.shouldBlur(keyCode); + if (blur_field && _this.filterInputBlur) { + _this.input.blur(); + } + return _this.options.query(_this.input.val(), function(data) { + return _this.options.callback(data); + }); + }, 250); + } else { + return _this.filter(_this.input.val()); + } + }; + })(this)); + } + + GitLabDropdownFilter.prototype.shouldBlur = function(keyCode) { + return BLUR_KEYCODES.indexOf(keyCode) >= 0; + }; + + GitLabDropdownFilter.prototype.filter = function(search_text) { + var data, elements, group, key, results, tmp; + if (this.options.onFilter) { + this.options.onFilter(search_text); + } + data = this.options.data(); + if ((data != null) && !this.options.filterByText) { + results = data; + if (search_text !== '') { + if (_.isArray(data)) { + results = fuzzaldrinPlus.filter(data, search_text, { + key: this.options.keys + }); + } else { + if (gl.utils.isObject(data)) { + results = {}; + for (key in data) { + group = data[key]; + tmp = fuzzaldrinPlus.filter(group, search_text, { + key: this.options.keys + }); + if (tmp.length) { + results[key] = tmp.map(function(item) { + return item; + }); + } + } + } + } + } + return this.options.callback(results); + } else { + elements = this.options.elements(); + if (search_text) { + return elements.each(function() { + var $el, matches; + $el = $(this); + matches = fuzzaldrinPlus.match($el.text().trim(), search_text); + if (!$el.is('.dropdown-header')) { + if (matches.length) { + return $el.show(); + } else { + return $el.hide(); + } + } + }); + } else { + return elements.show(); + } + } + }; + + return GitLabDropdownFilter; + + })(); + + GitLabDropdownRemote = (function() { + function GitLabDropdownRemote(dataEndpoint, options) { + this.dataEndpoint = dataEndpoint; + this.options = options; + } + + GitLabDropdownRemote.prototype.execute = function() { + if (typeof this.dataEndpoint === "string") { + return this.fetchData(); + } else if (typeof this.dataEndpoint === "function") { + if (this.options.beforeSend) { + this.options.beforeSend(); + } + return this.dataEndpoint("", (function(_this) { + return function(data) { + if (_this.options.success) { + _this.options.success(data); + } + if (_this.options.beforeSend) { + return _this.options.beforeSend(); + } + }; + })(this)); + } + }; + + GitLabDropdownRemote.prototype.fetchData = function() { + return $.ajax({ + url: this.dataEndpoint, + dataType: this.options.dataType, + beforeSend: (function(_this) { + return function() { + if (_this.options.beforeSend) { + return _this.options.beforeSend(); + } + }; + })(this), + success: (function(_this) { + return function(data) { + if (_this.options.success) { + return _this.options.success(data); + } + }; + })(this) + }); + }; + + return GitLabDropdownRemote; + + })(); + + GitLabDropdown = (function() { + var ACTIVE_CLASS, FILTER_INPUT, INDETERMINATE_CLASS, LOADING_CLASS, PAGE_TWO_CLASS, currentIndex; + + LOADING_CLASS = "is-loading"; + + PAGE_TWO_CLASS = "is-page-two"; + + ACTIVE_CLASS = "is-active"; + + INDETERMINATE_CLASS = "is-indeterminate"; + + currentIndex = -1; + + FILTER_INPUT = '.dropdown-input .dropdown-input-field'; + + function GitLabDropdown(el1, options) { + var ref, ref1, ref2, ref3, searchFields, selector, self; + this.el = el1; + this.options = options; + this.updateLabel = bind(this.updateLabel, this); + this.hidden = bind(this.hidden, this); + this.opened = bind(this.opened, this); + this.shouldPropagate = bind(this.shouldPropagate, this); + self = this; + selector = $(this.el).data("target"); + this.dropdown = selector != null ? $(selector) : $(this.el).parent(); + ref = this.options, this.filterInput = (ref1 = ref.filterInput) != null ? ref1 : this.getElement(FILTER_INPUT), this.highlight = (ref2 = ref.highlight) != null ? ref2 : false, this.filterInputBlur = (ref3 = ref.filterInputBlur) != null ? ref3 : true; + self = this; + if (_.isString(this.filterInput)) { + this.filterInput = this.getElement(this.filterInput); + } + searchFields = this.options.search ? this.options.search.fields : []; + if (this.options.data) { + if (_.isObject(this.options.data) && !_.isFunction(this.options.data)) { + this.fullData = this.options.data; + this.parseData(this.options.data); + } else { + this.remote = new GitLabDropdownRemote(this.options.data, { + dataType: this.options.dataType, + beforeSend: this.toggleLoading.bind(this), + success: (function(_this) { + return function(data) { + _this.fullData = data; + _this.parseData(_this.fullData); + if (_this.options.filterable && _this.filter && _this.filter.input) { + return _this.filter.input.trigger('keyup'); + } + }; + })(this) + }); + } + } + if (this.options.filterable) { + this.filter = new GitLabDropdownFilter(this.filterInput, { + filterInputBlur: this.filterInputBlur, + filterByText: this.options.filterByText, + onFilter: this.options.onFilter, + remote: this.options.filterRemote, + query: this.options.data, + keys: searchFields, + elements: (function(_this) { + return function() { + selector = '.dropdown-content li:not(.divider)'; + if (_this.dropdown.find('.dropdown-toggle-page').length) { + selector = ".dropdown-page-one " + selector; + } + return $(selector); + }; + })(this), + data: (function(_this) { + return function() { + return _this.fullData; + }; + })(this), + callback: (function(_this) { + return function(data) { + _this.parseData(data); + if (_this.filterInput.val() !== '') { + selector = '.dropdown-content li:not(.divider):visible'; + if (_this.dropdown.find('.dropdown-toggle-page').length) { + selector = ".dropdown-page-one " + selector; + } + $(selector, _this.dropdown).first().find('a').addClass('is-focused'); + return currentIndex = 0; + } + }; + })(this) + }); + } + this.dropdown.on("shown.bs.dropdown", this.opened); + this.dropdown.on("hidden.bs.dropdown", this.hidden); + $(this.el).on("update.label", this.updateLabel); + this.dropdown.on("click", ".dropdown-menu, .dropdown-menu-close", this.shouldPropagate); + this.dropdown.on('keyup', (function(_this) { + return function(e) { + if (e.which === 27) { + return $('.dropdown-menu-close', _this.dropdown).trigger('click'); + } + }; + })(this)); + this.dropdown.on('blur', 'a', (function(_this) { + return function(e) { + var $dropdownMenu, $relatedTarget; + if (e.relatedTarget != null) { + $relatedTarget = $(e.relatedTarget); + $dropdownMenu = $relatedTarget.closest('.dropdown-menu'); + if ($dropdownMenu.length === 0) { + return _this.dropdown.removeClass('open'); + } + } + }; + })(this)); + if (this.dropdown.find(".dropdown-toggle-page").length) { + this.dropdown.find(".dropdown-toggle-page, .dropdown-menu-back").on("click", (function(_this) { + return function(e) { + e.preventDefault(); + e.stopPropagation(); + return _this.togglePage(); + }; + })(this)); + } + if (this.options.selectable) { + selector = ".dropdown-content a"; + if (this.dropdown.find(".dropdown-toggle-page").length) { + selector = ".dropdown-page-one .dropdown-content a"; + } + this.dropdown.on("click", selector, function(e) { + var $el, selected; + $el = $(this); + selected = self.rowClicked($el); + if (self.options.clicked) { + self.options.clicked(selected, $el, e); + } + return $el.trigger('blur'); + }); + } + } + + GitLabDropdown.prototype.getElement = function(selector) { + return this.dropdown.find(selector); + }; + + GitLabDropdown.prototype.toggleLoading = function() { + return $('.dropdown-menu', this.dropdown).toggleClass(LOADING_CLASS); + }; + + GitLabDropdown.prototype.togglePage = function() { + var menu; + menu = $('.dropdown-menu', this.dropdown); + if (menu.hasClass(PAGE_TWO_CLASS)) { + if (this.remote) { + this.remote.execute(); + } + } + menu.toggleClass(PAGE_TWO_CLASS); + return this.dropdown.find('[class^="dropdown-page-"]:visible :text:visible:first').focus(); + }; + + GitLabDropdown.prototype.parseData = function(data) { + var full_html, groupData, html, name; + this.renderedData = data; + if (this.options.filterable && data.length === 0) { + html = [this.noResults()]; + } else { + if (gl.utils.isObject(data)) { + html = []; + for (name in data) { + groupData = data[name]; + html.push(this.renderItem({ + header: name + }, name)); + this.renderData(groupData, name).map(function(item) { + return html.push(item); + }); + } + } else { + html = this.renderData(data); + } + } + full_html = this.renderMenu(html); + return this.appendMenu(full_html); + }; + + GitLabDropdown.prototype.renderData = function(data, group) { + if (group == null) { + group = false; + } + return data.map((function(_this) { + return function(obj, index) { + return _this.renderItem(obj, group, index); + }; + })(this)); + }; + + GitLabDropdown.prototype.shouldPropagate = function(e) { + var $target; + if (this.options.multiSelect) { + $target = $(e.target); + if (!$target.hasClass('dropdown-menu-close') && !$target.hasClass('dropdown-menu-close-icon') && !$target.data('is-link')) { + e.stopPropagation(); + return false; + } else { + return true; + } + } + }; + + GitLabDropdown.prototype.opened = function() { + var contentHtml; + this.addArrowKeyEvent(); + if (this.options.setIndeterminateIds) { + this.options.setIndeterminateIds.call(this); + } + if (this.options.setActiveIds) { + this.options.setActiveIds.call(this); + } + if (this.fullData && this.dropdown.find('.dropdown-menu-toggle').hasClass('js-filter-bulk-update')) { + this.parseData(this.fullData); + } + contentHtml = $('.dropdown-content', this.dropdown).html(); + if (this.remote && contentHtml === "") { + this.remote.execute(); + } + if (this.options.filterable) { + this.filterInput.focus(); + } + return this.dropdown.trigger('shown.gl.dropdown'); + }; + + GitLabDropdown.prototype.hidden = function(e) { + var $input; + this.removeArrayKeyEvent(); + $input = this.dropdown.find(".dropdown-input-field"); + if (this.options.filterable) { + $input.blur().val(""); + } + if (!this.options.persistWhenHide) { + $input.trigger("keyup"); + } + if (this.dropdown.find(".dropdown-toggle-page").length) { + $('.dropdown-menu', this.dropdown).removeClass(PAGE_TWO_CLASS); + } + if (this.options.hidden) { + this.options.hidden.call(this, e); + } + return this.dropdown.trigger('hidden.gl.dropdown'); + }; + + GitLabDropdown.prototype.renderMenu = function(html) { + var menu_html; + menu_html = ""; + if (this.options.renderMenu) { + menu_html = this.options.renderMenu(html); + } else { + menu_html = $('<ul />').append(html); + } + return menu_html; + }; + + GitLabDropdown.prototype.appendMenu = function(html) { + var selector; + selector = '.dropdown-content'; + if (this.dropdown.find(".dropdown-toggle-page").length) { + selector = ".dropdown-page-one .dropdown-content"; + } + return $(selector, this.dropdown).empty().append(html); + }; + + GitLabDropdown.prototype.renderItem = function(data, group, index) { + var cssClass, field, fieldName, groupAttrs, html, selected, text, url, value; + if (group == null) { + group = false; + } + if (index == null) { + index = false; + } + html = ""; + if (data === "divider") { + return "<li class='divider'></li>"; + } + if (data === "separator") { + return "<li class='separator'></li>"; + } + if (data.header != null) { + return "<li class='dropdown-header'>" + data.header + "</li>"; + } + if (this.options.renderRow) { + html = this.options.renderRow.call(this.options, data, this); + } else { + if (!selected) { + value = this.options.id ? this.options.id(data) : data.id; + fieldName = this.options.fieldName; + field = this.dropdown.parent().find("input[name='" + fieldName + "'][value='" + value + "']"); + if (field.length) { + selected = true; + } + } + if (this.options.url != null) { + url = this.options.url(data); + } else { + url = data.url != null ? data.url : '#'; + } + if (this.options.text != null) { + text = this.options.text(data); + } else { + text = data.text != null ? data.text : ''; + } + cssClass = ""; + if (selected) { + cssClass = "is-active"; + } + if (this.highlight) { + text = this.highlightTextMatches(text, this.filterInput.val()); + } + if (group) { + groupAttrs = "data-group='" + group + "' data-index='" + index + "'"; + } else { + groupAttrs = ''; + } + html = "<li> <a href='" + url + "' " + groupAttrs + " class='" + cssClass + "'> " + text + " </a> </li>"; + } + return html; + }; + + GitLabDropdown.prototype.highlightTextMatches = function(text, term) { + var occurrences; + occurrences = fuzzaldrinPlus.match(text, term); + return text.split('').map(function(character, i) { + if (indexOf.call(occurrences, i) >= 0) { + return "<b>" + character + "</b>"; + } else { + return character; + } + }).join(''); + }; + + GitLabDropdown.prototype.noResults = function() { + var html; + return html = "<li class='dropdown-menu-empty-link'> <a href='#' class='is-focused'> No matching results. </a> </li>"; + }; + + GitLabDropdown.prototype.highlightRow = function(index) { + var selector; + if (this.filterInput.val() !== "") { + selector = '.dropdown-content li:first-child a'; + if (this.dropdown.find(".dropdown-toggle-page").length) { + selector = ".dropdown-page-one .dropdown-content li:first-child a"; + } + return this.getElement(selector).addClass('is-focused'); + } + }; + + GitLabDropdown.prototype.rowClicked = function(el) { + var field, fieldName, groupName, isInput, selectedIndex, selectedObject, value; + fieldName = this.options.fieldName; + isInput = $(this.el).is('input'); + if (this.renderedData) { + groupName = el.data('group'); + if (groupName) { + selectedIndex = el.data('index'); + selectedObject = this.renderedData[groupName][selectedIndex]; + } else { + selectedIndex = el.closest('li').index(); + selectedObject = this.renderedData[selectedIndex]; + } + } + value = this.options.id ? this.options.id(selectedObject, el) : selectedObject.id; + if (isInput) { + field = $(this.el); + } else { + field = this.dropdown.parent().find("input[name='" + fieldName + "'][value='" + value + "']"); + } + if (el.hasClass(ACTIVE_CLASS)) { + el.removeClass(ACTIVE_CLASS); + if (isInput) { + field.val(''); + } else { + field.remove(); + } + if (this.options.toggleLabel) { + return this.updateLabel(selectedObject, el, this); + } else { + return selectedObject; + } + } else if (el.hasClass(INDETERMINATE_CLASS)) { + el.addClass(ACTIVE_CLASS); + el.removeClass(INDETERMINATE_CLASS); + if (value == null) { + field.remove(); + } + if (!field.length && fieldName) { + this.addInput(fieldName, value); + } + return selectedObject; + } else { + if (!this.options.multiSelect || el.hasClass('dropdown-clear-active')) { + this.dropdown.find("." + ACTIVE_CLASS).removeClass(ACTIVE_CLASS); + if (!isInput) { + this.dropdown.parent().find("input[name='" + fieldName + "']").remove(); + } + } + if (value == null) { + field.remove(); + } + el.addClass(ACTIVE_CLASS); + if (this.options.toggleLabel) { + this.updateLabel(selectedObject, el, this); + } + if (value != null) { + if (!field.length && fieldName) { + this.addInput(fieldName, value); + } else { + field.val(value).trigger('change'); + } + } + return selectedObject; + } + }; + + GitLabDropdown.prototype.addInput = function(fieldName, value) { + var $input; + $input = $('<input>').attr('type', 'hidden').attr('name', fieldName).val(value); + if (this.options.inputId != null) { + $input.attr('id', this.options.inputId); + } + return this.dropdown.before($input); + }; + + GitLabDropdown.prototype.selectRowAtIndex = function(e, index) { + var $el, selector; + selector = ".dropdown-content li:not(.divider,.dropdown-header,.separator):eq(" + index + ") a"; + if (this.dropdown.find(".dropdown-toggle-page").length) { + selector = ".dropdown-page-one " + selector; + } + $el = $(selector, this.dropdown); + if ($el.length) { + e.preventDefault(); + e.stopImmediatePropagation(); + return $el.first().trigger('click'); + } + }; + + GitLabDropdown.prototype.addArrowKeyEvent = function() { + var $input, ARROW_KEY_CODES, selector; + ARROW_KEY_CODES = [38, 40]; + $input = this.dropdown.find(".dropdown-input-field"); + selector = '.dropdown-content li:not(.divider,.dropdown-header,.separator)'; + if (this.dropdown.find(".dropdown-toggle-page").length) { + selector = ".dropdown-page-one " + selector; + } + return $('body').on('keydown', (function(_this) { + return function(e) { + var $listItems, PREV_INDEX, currentKeyCode; + currentKeyCode = e.which; + if (ARROW_KEY_CODES.indexOf(currentKeyCode) >= 0) { + e.preventDefault(); + e.stopImmediatePropagation(); + PREV_INDEX = currentIndex; + $listItems = $(selector, _this.dropdown); + if (currentKeyCode === 40) { + if (currentIndex < ($listItems.length - 1)) { + currentIndex += 1; + } + } else if (currentKeyCode === 38) { + if (currentIndex > 0) { + currentIndex -= 1; + } + } + if (currentIndex !== PREV_INDEX) { + _this.highlightRowAtIndex($listItems, currentIndex); + } + return false; + } + if (currentKeyCode === 13 && currentIndex !== -1) { + return _this.selectRowAtIndex(e, currentIndex); + } + }; + })(this)); + }; + + GitLabDropdown.prototype.removeArrayKeyEvent = function() { + return $('body').off('keydown'); + }; + + GitLabDropdown.prototype.highlightRowAtIndex = function($listItems, index) { + var $dropdownContent, $listItem, dropdownContentBottom, dropdownContentHeight, dropdownContentTop, dropdownScrollTop, listItemBottom, listItemHeight, listItemTop; + $('.is-focused', this.dropdown).removeClass('is-focused'); + $listItem = $listItems.eq(index); + $listItem.find('a:first-child').addClass("is-focused"); + $dropdownContent = $listItem.closest('.dropdown-content'); + dropdownScrollTop = $dropdownContent.scrollTop(); + dropdownContentHeight = $dropdownContent.outerHeight(); + dropdownContentTop = $dropdownContent.prop('offsetTop'); + dropdownContentBottom = dropdownContentTop + dropdownContentHeight; + listItemHeight = $listItem.outerHeight(); + listItemTop = $listItem.prop('offsetTop'); + listItemBottom = listItemTop + listItemHeight; + if (listItemBottom > dropdownContentBottom + dropdownScrollTop) { + return $dropdownContent.scrollTop(listItemBottom - dropdownContentBottom); + } else if (listItemTop < dropdownContentTop + dropdownScrollTop) { + return $dropdownContent.scrollTop(listItemTop - dropdownContentTop); + } + }; + + GitLabDropdown.prototype.updateLabel = function(selected, el, instance) { + if (selected == null) { + selected = null; + } + if (el == null) { + el = null; + } + if (instance == null) { + instance = null; + } + return $(this.el).find(".dropdown-toggle-text").text(this.options.toggleLabel(selected, el, instance)); + }; + + return GitLabDropdown; + + })(); + + $.fn.glDropdown = function(opts) { + return this.each(function() { + if (!$.data(this, 'glDropdown')) { + return $.data(this, 'glDropdown', new GitLabDropdown(this, opts)); + } + }); + }; + +}).call(this); |