/** * @summary SelectMenu * @desc Simple, easily and diversity menu solution * @file selectmenu.js * @version 2.1 * @author TerryZeng * @contact https://terryz.github.io/ * @license MIT License * * depend on: * jQuery1.x * */ ;(function($){ "use strict"; /** * Version: v3.6.1 * The MIT License: Copyright (c) 2010-2017 LiosK. * Link: https://github.com/LiosK/UUID.js */ var UUID; UUID=function(f){function a(){}a.generate=function(){var b=a._getRandomInt,c=a._hexAligner;return c(b(32),8)+"-"+c(b(16),4)+"-"+c(16384|b(12),4)+"-"+c(32768|b(14),4)+"-"+c(b(48),12)};a._getRandomInt=function(b){if(0>b||53>>30-b};a._hexAligner=function(b,c){for(var a=b.toString(16),d=c-a.length,e="0";0>>=1,e+=e)d&1&&(a=e+a);return a};a.overwrittenUUID=f;"undefined"!==typeof module&&module&&module.exports&& (module.exports=a);return a}(UUID); /** * Default options */ var defaults = { /** * Menu data source * @type array | function * @example * array:[{a:1,b:2,c:3},{...}] * function: function(){ return [{...}];} * return data format is same to array */ data: undefined, /** * Show quick search input element, work on advance menu mode * @type boolean * @default true */ search : true, /** * Title bar text, set false to close title bar * @type string | boolean * @default 'SelectMenu' */ title : 'SelectMenu', /** * Regular menu mode * @type boolean * @default false */ regular : false, /** * Mouse right click to show menu * @type boolean * @default false */ rightClick : false, /** * Menu show arrow, look like a bubble * @type boolean * @default false */ arrow : false, /** * Alignment direction * @type string * @enum * 'left' * 'center' * 'right' * @default 'left' */ position : 'left', /** * Embedded to page * @type boolean * @default false */ embed : false, /** * Language ('cn', 'ja', 'en', 'es', 'pt-br') * @type string * @default 'cn' */ lang: 'cn', /** * Multiple select mode(tags) * @type boolean * @default false */ multiple: false, /** * Menu result list size, the number mean is visible menu item amount * @type number * @default 10 */ listSize : 10, /** * Maximum selected item limit in multiple select mode, set 0 to unlimited * @type number * @default 0 (unlimited) */ maxSelectLimit: 0, /** * Close result list after menu item selected, work on multiple select mode * @type boolean * @default true */ selectToCloseList: false, /** * Set a key to selected menu item when plugin init complete, work on multiple select mode * the key will match to keyField * @type string */ initSelected: undefined, /** * Key field to return data * @type string * @default 'id' */ keyField: 'id', /** * Show content field * @type string * @default 'name' */ showField: 'name', /** * Data field in search, not set default used showField * @type string */ searchField : undefined, /** * Filter type ('AND' or 'OR') * @type string default: 'AND' */ andOr: 'AND', /** * Sort order, not set default used showField * @type array * @example * orderBy : ['id desc']//order by id desc */ orderBy: undefined, /** * Max item size * @type number */ pageSize: 100, /** * Menu item result format * @type function * @param data {object} menu item data * @return string */ formatItem : undefined, /** * -----------------------------------------Event-------------------------------------------- */ /** * Menu item select callback * @type function * @param data {array[Object]} selected items data */ eSelect : undefined, /** * Multiple group data type tab switch callback * @type function * @param index {number} */ eTabSwitch : undefined, /** * Menu hide callback * @type function * @param data {array[Object]} selected items data */ eHidden : undefined }; /** * @constructor * @param {Object} input - menu caller * @param {Object} option - menu init option */ var SelectMenu = function(input, option) { this.target = input; this.setOption(option); if(this.option.embed && !$(input).is('div')){ console.warn('SelectMenu embed mode need a "div" container element!'); return; } this.setLanguage(); this.setCssClass(); this.setProp(); if(option.regular) this.setRegularMenu(); else this.setElem(); if(!option.rightClick) this.populate(); this.eInput(); if(!option.embed) this.eWhole(); this.atLast(); }; /** * Plugin version number */ SelectMenu.version = '2.1'; /** * Plugin object cache key */ SelectMenu.dataKey = 'selectMenuObject'; /** * Data source type * List type */ SelectMenu.dataTypeList = 'SelectMenuList'; /** * Group type */ SelectMenu.dataTypeGroup = 'SelectMenuGroup'; /** * Regular menu type */ SelectMenu.dataTypeMenu = 'SelectMenuMenu'; /** * Initial plugin option * @param {Object} option */ SelectMenu.prototype.setOption = function(option) { //if not set, default used showField set field option.searchField = option.searchField || option.showField; if(option.regular && option.title === defaults.title) option.title = false; //Close arrow in embed and mouse right click mode if(option.embed || option.rightClick) option.arrow = false; option.andOr = option.andOr.toUpperCase(); if(option.andOr!=='AND' && option.andOr!=='OR') option.andOr = 'AND'; option.orderBy = (option.orderBy === undefined) ? option.showField : option.orderBy; //Multiple field sort //Example: [ ['id', 'ASC'], ['name', 'DESC'] ] option.orderBy = this.setOrderbyOption(option.orderBy, option.showField); if($.type(option.data) === 'string'){ option.autoSelectFirst = false; } if($.type(option.listSize) !== 'number' || option.listSize < 0) option.listSize = 12; this.option = option; }; /** * Initial order * @param {Array} arg_order * @param {string} arg_field * @return {Array} */ SelectMenu.prototype.setOrderbyOption = function(arg_order, arg_field) { var arr = [],orders = []; if (typeof arg_order == 'object') { for (var i = 0; i < arg_order.length; i++) { orders = $.trim(arg_order[i]).split(' '); arr[i] = (orders.length == 2) ? orders: [orders[0], 'ASC']; } } else { orders = $.trim(arg_order).split(' '); arr[0] = (orders.length == 2) ? orders: (orders[0].match(/^(ASC|DESC)$/i)) ? [arg_field, orders[0]] : [orders[0], 'ASC']; } return arr; }; /** * i18n */ SelectMenu.prototype.setLanguage = function() { var message; switch (this.option.lang) { // 中文 case 'cn': message = { select_all_btn: '选择所有 (或当前页签) 项目', remove_all_btn: '清除所有选中的项目', close_btn: '关闭菜单 (Esc键)', loading: '读取中...', select_ng: '请注意:请从列表中选择.', select_ok: 'OK : 已经选择.', not_found: '无查询结果', ajax_error: '连接到服务器时发生错误!', max_selected: '最多只能选择 max_selected_limit 个项目' }; break; // English case 'en': message = { select_all_btn: 'Select All (Tabs) items', remove_all_btn: 'Clear all selected items', close_btn: 'Close Menu (Esc key)', loading: 'loading...', select_ng: 'Attention : Please choose from among the list.', select_ok: 'OK : Correctly selected.', not_found: 'not found', ajax_error: 'An error occurred while connecting to server.', max_selected: 'You can only select up to max_selected_limit items' }; break; // Japanese case 'ja': message = { select_all_btn: 'すべての (または現在のタブ) 項目を選択', remove_all_btn: '選択したすべての項目をクリアする', close_btn: '閉じる (Tabキー)', loading: '読み込み中...', select_ng: '注意 : リストの中から選択してください', select_ok: 'OK : 正しく選択されました。', not_found: '(0 件)', ajax_error: 'サーバとの通信でエラーが発生しました。', max_selected: '最多で max_selected_limit のプロジェクトを選ぶことしかできません' }; break; // German case 'de': message = { select_all_btn: 'Wählen Sie alle (oder aktuellen Registerkarten) aus', remove_all_btn: 'Alle ausgewählten Elemente löschen', close_btn: 'Schließen (Tab)', loading: 'lade...', select_ng: 'Achtung: Bitte wählen Sie aus der Liste aus.', select_ok: 'OK : Richtig ausgewählt.', not_found: 'nicht gefunden', ajax_error: 'Bei der Verbindung zum Server ist ein Fehler aufgetreten.', max_selected: 'Sie können nur bis zu max_selected_limit Elemente auswählen' }; break; // Spanish case 'es': message = { select_all_btn: 'Seleccionar todos los elementos (o la pestaña actual)', remove_all_btn: 'Borrar todos los elementos seleccionados', close_btn: 'Cerrar (tecla TAB)', loading: 'Cargando...', select_ng: 'Atencion: Elija una opcion de la lista.', select_ok: 'OK: Correctamente seleccionado.', not_found: 'no encuentre', ajax_error: 'Un error ocurrió mientras conectando al servidor.', max_selected: 'Solo puedes seleccionar hasta max_selected_limit elementos' }; break; // Brazilian Portuguese case 'pt-br': message = { select_all_btn: 'Selecione todos os itens (ou guia atual)', remove_all_btn: 'Limpe todos os itens selecionados', close_btn: 'Fechar (tecla TAB)', loading: 'Carregando...', select_ng: 'Atenção: Escolha uma opção da lista.', select_ok: 'OK: Selecionado Corretamente.', not_found: 'não encontrado', ajax_error: 'Um erro aconteceu enquanto conectando a servidor.', max_selected: 'Você só pode selecionar até max_selected_limit itens' }; break; } this.message = message; }; /** * CSS classname set */ SelectMenu.prototype.setCssClass = function() { var css_class = { target_clicked : 'sm_target_clicked', container: 'sm_container', container_open: 'sm_container_open', container_embed: 'sm_embed', header: 'sm_header', re_area: 'sm_result_area', re_tabs: 'sm_result_tabs', re_list: 'sm_list_mode', control_box: 'sm_control_box', two_btn: 'sm_two_btn', element_box: 'sm_element_box', results: 'sm_results', re_off: 'sm_results_off', select: 'sm_over', selected_icon: 'sm_selected_icon', item_text: 'sm_item_text', select_ok: 'sm_select_ok', select_ng: 'sm_select_ng', selected: 'sm_selected', input_off: 'sm_input_off', message_box: 'sm_message_box', btn_close: 'sm_close_button', btn_selectall: 'sm_selectall_button', btn_removeall: 'sm_removeall_button', btn_on: 'sm_btn_on', btn_out: 'sm_btn_out', btn_back: 'sm_sub_back', input: 'sm_input', input_area: 'sm_input_area', clear_btn: 'sm_clear_btn', menu_root: 'sm_menu_root', menu_divider: 'sm_divider', menu_regular: 'sm_regular', menu_arrow: 'sm_arrow', menu_arrow_have_title : 'sm_have_title', menu_disabled: 'sm_disabled', menu_header: 'sm_header', menu_caret: 'sm_caret', menu_sub_menu: 'sm_sub_menu', menu_sub_item: 'sm_sub_item', menu_sub_header: 'sm_sub_header', direction_top : 'sm_arrow_top', direction_bottom : 'sm_arrow_bottom' }; this.css_class = css_class; this.template = { msg :{ maxSelectLimit: 'max_selected_limit' } }; }; /** * Internal variable initial */ SelectMenu.prototype.setProp = function() { this.prop = { //selected menu item keys values : [], data : undefined, //multiple group data current data index data_index : 0, key_select: false, prev_value: '', selected_text : '', last_input_time: undefined, //menu data type data_type : SelectMenu.dataTypeList, //id prefix menu_tab_id_prefix : 'selectmenu_tab_', menu_code_prefix: 'selectmenu_', //mouse x point x : undefined, //mouse y point y : undefined }; }; /** * Data source type check */ SelectMenu.prototype.checkDataType = function(d){ var self = this,p = this.option; if(d && $.isArray(d) && d.length){ if(p.regular) return SelectMenu.dataTypeMenu; else{ var row = d[0]; if(row.hasOwnProperty('title') && row.hasOwnProperty('list') && $.isArray(row.list)){ return SelectMenu.dataTypeGroup; }else return SelectMenu.dataTypeList; } }else return null; }; /** * Menu structure build */ SelectMenu.prototype.setElem = function() { var self = this, p = this.option, css = this.css_class; // 1. build dom element var elem = {}; elem.container = p.embed ? $(self.target).addClass(css.container_embed) : $('
'); elem.container.addClass(css.container).addClass(css.direction_bottom); if(p.title){ elem.header = $('
').addClass(css.header); elem.header.append('

' + p.title + '

'); if(p.multiple){ elem.selectAllButton = $('') .attr('title',this.message.select_all_btn) .addClass(css.btn_selectall); elem.removeAllButton = $('') .attr('title',this.message.remove_all_btn) .addClass(css.btn_removeall); elem.header.append(elem.selectAllButton); elem.header.append(elem.removeAllButton); } if(!p.embed){ elem.closeButton = $('') .attr('title',self.message.close_btn) .addClass(css.btn_close); elem.header.append(elem.closeButton); } } elem.inputArea = $('
').addClass(css.input_area); elem.input = $('').addClass(css.input); //Result list elem.resultArea = $('
').addClass(css.re_area); elem.resultTabs = $('
').addClass(css.re_tabs); elem.results = $('