NEW Add lib for multiselect with checkboxes

NEW Can select several prospect level in thirdparty filter.
Remove old multiselect lib that was not used by core.
This commit is contained in:
Laurent Destailleur 2018-09-15 13:31:25 +02:00
parent 80fb1e0657
commit bbfe4462dc
10 changed files with 606 additions and 642 deletions

View File

@ -5730,7 +5730,7 @@ class Form
* @param string $morecss Add more class to css styles
* @param int $callurlonselect If set to 1, some code is added so an url return by the ajax is called when value is selected.
* @param string $placeholder String to use as placeholder
* @param integer $acceptdelayedhtml 1 if caller request to have html js content not returned but saved into global $delayedhtmlcontent (so caller can show it at end of page to avoid flash FOUC effect)
* @param integer $acceptdelayedhtml 1 = caller is requesting to have html js content not returned but saved into global $delayedhtmlcontent (so caller can show it at end of page to avoid flash FOUC effect)
* @return string HTML select string
* @see selectArrayFilter, ajax_combobox in ajax.lib.php
*/
@ -5832,7 +5832,7 @@ class Form
* @param string $morecss Add more class to css styles
* @param int $callurlonselect If set to 1, some code is added so an url return by the ajax is called when value is selected.
* @param string $placeholder String to use as placeholder
* @param integer $acceptdelayedhtml 1 if caller request to have html js content not returned but saved into global $delayedhtmlcontent (so caller can show it at end of page to avoid flash FOUC effect)
* @param integer $acceptdelayedhtml 1 = caller is requesting to have html js content not returned but saved into global $delayedhtmlcontent (so caller can show it at end of page to avoid flash FOUC effect)
* @return string HTML select string
* @see selectArrayAjax, ajax_combobox in ajax.lib.php
*/
@ -5949,55 +5949,76 @@ class Form
* @param int $width Force width of select box. May be used only when using jquery couch. Example: 250, 95%
* @param string $moreattrib Add more options on select component. Example: 'disabled'
* @param string $elemtype Type of element we show ('category', ...)
* @param string $placeholder String to use as placeholder
* @param int $addjscombo Add js combo
* @return string HTML multiselect string
* @see selectarray
*/
static function multiselectarray($htmlname, $array, $selected=array(), $key_in_label=0, $value_as_key=0, $morecss='', $translate=0, $width=0, $moreattrib='',$elemtype='')
static function multiselectarray($htmlname, $array, $selected=array(), $key_in_label=0, $value_as_key=0, $morecss='', $translate=0, $width=0, $moreattrib='', $elemtype='', $placeholder='', $addjscombo=1)
{
global $conf, $langs;
$out = '';
// Add code for jquery to use multiselect
if (! empty($conf->global->MAIN_USE_JQUERY_MULTISELECT) || defined('REQUIRE_JQUERY_MULTISELECT'))
{
$tmpplugin=empty($conf->global->MAIN_USE_JQUERY_MULTISELECT)?constant('REQUIRE_JQUERY_MULTISELECT'):$conf->global->MAIN_USE_JQUERY_MULTISELECT;
$out.="\n".'<!-- JS CODE TO ENABLE '.$tmpplugin.' for id '.$htmlname.' -->
<script type="text/javascript">
function formatResult(record) {'."\n";
if ($elemtype == 'category')
{
$out.=' //return \'<span><img src="'.DOL_URL_ROOT.'/theme/eldy/img/object_category.png'.'"> <a href="'.DOL_URL_ROOT.'/categories/viewcat.php?type=0&id=\'+record.id+\'">\'+record.text+\'</a></span>\';
return \'<span><img src="'.DOL_URL_ROOT.'/theme/eldy/img/object_category.png'.'"> \'+record.text+\'</span>\';';
}
else
{
$out.='return record.text;';
}
$out.= ' };
function formatSelection(record) {'."\n";
if ($elemtype == 'category')
{
$out.=' //return \'<span><img src="'.DOL_URL_ROOT.'/theme/eldy/img/object_category.png'.'"> <a href="'.DOL_URL_ROOT.'/categories/viewcat.php?type=0&id=\'+record.id+\'">\'+record.text+\'</a></span>\';
return \'<span><img src="'.DOL_URL_ROOT.'/theme/eldy/img/object_category.png'.'"> \'+record.text+\'</span>\';';
}
else
{
$out.='return record.text;';
}
$out.= ' };
$(document).ready(function () {
$(\'#'.$htmlname.'\').'.$tmpplugin.'({
dir: \'ltr\',
// Specify format function for dropdown item
formatResult: formatResult,
templateResult: formatResult, /* For 4.0 */
// Specify format function for selected item
formatSelection: formatSelection,
templateResult: formatSelection /* For 4.0 */
});
});
</script>';
$out.="\n".'<!-- JS CODE TO ENABLE '.$tmpplugin.' for id '.$htmlname.' -->
<script type="text/javascript">'."\n";
if ($addjscombo == 1)
{
$tmpplugin=empty($conf->global->MAIN_USE_JQUERY_MULTISELECT)?constant('REQUIRE_JQUERY_MULTISELECT'):$conf->global->MAIN_USE_JQUERY_MULTISELECT;
$out.= 'function formatResult(record) {'."\n";
if ($elemtype == 'category')
{
$out.=' //return \'<span><img src="'.DOL_URL_ROOT.'/theme/eldy/img/object_category.png'.'"> <a href="'.DOL_URL_ROOT.'/categories/viewcat.php?type=0&id=\'+record.id+\'">\'+record.text+\'</a></span>\';
return \'<span><img src="'.DOL_URL_ROOT.'/theme/eldy/img/object_category.png'.'"> \'+record.text+\'</span>\';';
}
else
{
$out.='return record.text;';
}
$out.= '};'."\n";
$out.= 'function formatSelection(record) {'."\n";
if ($elemtype == 'category')
{
$out.=' //return \'<span><img src="'.DOL_URL_ROOT.'/theme/eldy/img/object_category.png'.'"> <a href="'.DOL_URL_ROOT.'/categories/viewcat.php?type=0&id=\'+record.id+\'">\'+record.text+\'</a></span>\';
return \'<span><img src="'.DOL_URL_ROOT.'/theme/eldy/img/object_category.png'.'"> \'+record.text+\'</span>\';';
}
else
{
$out.='return record.text;';
}
$out.= '};'."\n";
$out.= '$(document).ready(function () {
$(\'#'.$htmlname.'\').'.$tmpplugin.'({
dir: \'ltr\',
// Specify format function for dropdown item
formatResult: formatResult,
templateResult: formatResult, /* For 4.0 */
// Specify format function for selected item
formatSelection: formatSelection,
templateResult: formatSelection /* For 4.0 */
});
});'."\n";
}
elseif ($addjscombo == 2)
{
// Add other js lib
// ...
$out.= '$(document).ready(function () {
$(\'#'.$htmlname.'\').multiSelect({
containerHTML: \'<div class="multi-select-container">\',
menuHTML: \'<div class="multi-select-menu">\',
buttonHTML: \'<span class="multi-select-button '.$morecss.'">\',
menuItemHTML: \'<label class="multi-select-menuitem">\',
activeClass: \'multi-select-container--open\',
noneText: \''.$placeholder.'\'
});
})';
}
$out.= '</script>';
}
// Try also magic suggest

View File

@ -1,20 +0,0 @@
Copyright (c) 2009 Michael Aufreiter, http://www.quasipartikel.at
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -1,144 +0,0 @@
/* Multiselect
----------------------------------*/
.multiselect {
width: 600px;
height: 150px;
}
.ui-multiselect {
border: solid 1px;
font-size: 0.8em;
}
.ui-multiselect ul {
-moz-user-select: none;
}
.ui-multiselect li {
margin: 0;
padding: 0;
cursor: default;
line-height: 20px;
height: 20px;
font-size: 11px;
list-style: none;
}
.ui-multiselect li a {
color: #999;
text-decoration: none;
padding: 0;
display: block;
float: left;
cursor: pointer;
}
.ui-multiselect li.ui-draggable-dragging {
padding-left: 10px;
}
.ui-multiselect div.selected {
position: relative;
padding: 0;
margin: 0;
border: 0;
float: left;
}
.ui-multiselect ul.selected {
position: relative;
padding: 0;
overflow: auto;
overflow-x: hidden;
background: #fff;
margin: 0;
list-style: none;
border: 0;
position: relative;
width: 100%;
}
.ui-multiselect ul.selected li {
}
.ui-multiselect div.available {
position: relative;
padding: 0;
margin: 0;
border: 0;
float: left;
border-left: 1px solid;
}
.ui-multiselect ul.available {
position: relative;
padding: 0;
overflow: auto;
overflow-x: hidden;
background: #fff;
margin: 0;
list-style: none;
border: 0;
width: 100%;
}
.ui-multiselect ul.available li {
padding-left: 10px;
}
.ui-multiselect .ui-state-default {
border: none;
margin-bottom: 1px;
position: relative;
padding-left: 20px;
}
.ui-multiselect .ui-state-hover {
border: none;
}
.ui-multiselect .ui-widget-header {
border: none;
font-size: 11px;
margin-bottom: 1px;
}
.ui-multiselect .add-all {
float: right;
padding: 7px;
}
.ui-multiselect .remove-all {
float: right;
padding: 7px;
}
.ui-multiselect .search {
float: left;
padding: 4px;
}
.ui-multiselect .count {
float: left;
padding: 7px;
}
.ui-multiselect li span.ui-icon-arrowthick-2-n-s {
position: absolute;
left: 2px;
}
.ui-multiselect li a.action {
position: absolute;
right: 2px;
top: 2px;
}
.ui-multiselect input.search {
height: 14px;
padding: 1px;
opacity: 0.5;
margin: 4px;
width: 100px;
}

View File

@ -0,0 +1,360 @@
// jquery.multi-select.js
// by mySociety
// https://github.com/mysociety/jquery-multi-select
;(function($) {
"use strict";
var pluginName = "multiSelect",
defaults = {
'containerHTML': '<div class="multi-select-container">',
'menuHTML': '<div class="multi-select-menu">',
'buttonHTML': '<span class="multi-select-button">',
'menuItemsHTML': '<div class="multi-select-menuitems">',
'menuItemHTML': '<label class="multi-select-menuitem">',
'presetsHTML': '<div class="multi-select-presets">',
'activeClass': 'multi-select-container--open',
'noneText': '-- Select --',
'allText': undefined,
'presets': undefined,
'positionedMenuClass': 'multi-select-container--positioned',
'positionMenuWithin': undefined,
'viewportBottomGutter': 20,
'menuMinHeight': 200
};
/**
* @constructor
*/
function MultiSelect(element, options) {
this.element = element;
this.$element = $(element);
this.settings = $.extend( {}, defaults, options );
this._defaults = defaults;
this._name = pluginName;
this.init();
}
function arraysAreEqual(array1, array2) {
if ( array1.length != array2.length ){
return false;
}
array1.sort();
array2.sort();
for ( var i = 0; i < array1.length; i++ ){
if ( array1[i] !== array2[i] ){
return false;
}
}
return true;
}
$.extend(MultiSelect.prototype, {
init: function() {
this.checkSuitableInput();
this.findLabels();
this.constructContainer();
this.constructButton();
this.constructMenu();
this.setUpBodyClickListener();
this.setUpLabelsClickListener();
this.$element.hide();
},
checkSuitableInput: function(text) {
if ( this.$element.is('select[multiple]') === false ) {
throw new Error('$.multiSelect only works on <select multiple> elements');
}
},
findLabels: function() {
this.$labels = $('label[for="' + this.$element.attr('id') + '"]');
},
constructContainer: function() {
this.$container = $(this.settings['containerHTML']);
this.$element.data('multi-select-container', this.$container);
this.$container.insertAfter(this.$element);
},
constructButton: function() {
var _this = this;
this.$button = $(this.settings['buttonHTML']);
this.$button.attr({
'role': 'button',
'aria-haspopup': 'true',
'tabindex': 0,
'aria-label': this.$labels.eq(0).text()
})
.on('keydown.multiselect', function(e) {
var key = e.which;
var returnKey = 13;
var spaceKey = 32;
if ((key === returnKey) || (key === spaceKey)) {
_this.$button.click();
}
}).on('click.multiselect', function(e) {
_this.menuToggle();
})
.appendTo(this.$container);
this.$element.on('change.multiselect', function() {
_this.updateButtonContents();
});
this.updateButtonContents();
},
updateButtonContents: function() {
var _this = this;
var options = [];
var selected = [];
this.$element.children('option').each(function() {
var text = /** @type string */ ($(this).text());
options.push(text);
if ($(this).is(':selected')) {
selected.push( $.trim(text) );
}
});
this.$button.empty();
if (selected.length == 0) {
this.$button.text( this.settings['noneText'] );
} else if ( (selected.length === options.length) && this.settings['allText']) {
this.$button.text( this.settings['allText'] );
} else {
this.$button.text( selected.join(', ') );
}
},
constructMenu: function() {
var _this = this;
this.$menu = $(this.settings['menuHTML']);
this.$menu.attr({
'role': 'menu'
}).on('keyup.multiselect', function(e){
var key = e.which;
var escapeKey = 27;
if (key === escapeKey) {
_this.menuHide();
}
})
.appendTo(this.$container);
this.constructMenuItems();
if ( this.settings['presets'] ) {
this.constructPresets();
}
},
constructMenuItems: function() {
var _this = this;
this.$menuItems = $(this.settings['menuItemsHTML']);
this.$menu.append(this.$menuItems);
this.$element.on('change.multiselect', function(e, internal) {
// Don't need to update the menu items if this
// change event was fired by our tickbox handler.
if(internal !== true){
_this.updateMenuItems();
}
});
this.updateMenuItems();
},
updateMenuItems: function() {
var _this = this;
this.$menuItems.empty();
this.$element.children('option').each(function(option_index, option) {
var $item = _this.constructMenuItem($(option), option_index);
_this.$menuItems.append($item);
});
},
constructPresets: function() {
var _this = this;
this.$presets = $(this.settings['presetsHTML']);
this.$menu.prepend(this.$presets);
$.each(this.settings['presets'], function(i, preset){
var unique_id = _this.$element.attr('name') + '_preset_' + i;
var $item = $(_this.settings['menuItemHTML'])
.attr({
'for': unique_id,
'role': 'menuitem'
})
.text(' ' + preset.name)
.appendTo(_this.$presets);
var $input = $('<input>')
.attr({
'type': 'radio',
'name': _this.$element.attr('name') + '_presets',
'id': unique_id
})
.prependTo($item);
$input.on('change.multiselect', function(){
_this.$element.val(preset.options);
_this.$element.trigger('change');
});
});
this.$element.on('change.multiselect', function() {
_this.updatePresets();
});
this.updatePresets();
},
updatePresets: function() {
var _this = this;
$.each(this.settings['presets'], function(i, preset){
var unique_id = _this.$element.attr('name') + '_preset_' + i;
var $input = _this.$presets.find('#' + unique_id);
if ( arraysAreEqual(preset.options || [], _this.$element.val() || []) ){
$input.prop('checked', true);
} else {
$input.prop('checked', false);
}
});
},
constructMenuItem: function($option, option_index) {
var unique_id = this.$element.attr('name') + '_' + option_index;
var $item = $(this.settings['menuItemHTML'])
.attr({
'for': unique_id,
'role': 'menuitem'
})
.text(' ' + $option.text());
var $input = $('<input>')
.attr({
'type': 'checkbox',
'id': unique_id,
'value': $option.val()
})
.prependTo($item);
if ( $option.is(':disabled') ) {
$input.attr('disabled', 'disabled');
}
if ( $option.is(':selected') ) {
$input.prop('checked', 'checked');
}
$input.on('change.multiselect', function() {
if ($(this).prop('checked')) {
$option.prop('selected', true);
} else {
$option.prop('selected', false);
}
// .prop() on its own doesn't generate a change event.
// Other plugins might want to do stuff onChange.
$option.trigger('change', [true]);
});
return $item;
},
setUpBodyClickListener: function() {
var _this = this;
// Hide the $menu when you click outside of it.
$('html').on('click.multiselect', function(){
_this.menuHide();
});
// Stop click events from inside the $button or $menu from
// bubbling up to the body and closing the menu!
this.$container.on('click.multiselect', function(e){
e.stopPropagation();
});
},
setUpLabelsClickListener: function() {
var _this = this;
this.$labels.on('click.multiselect', function(e) {
e.preventDefault();
e.stopPropagation();
_this.menuToggle();
});
},
menuShow: function() {
$('html').trigger('click.multiselect'); // Close any other open menus
this.$container.addClass(this.settings['activeClass']);
if ( this.settings['positionMenuWithin'] && this.settings['positionMenuWithin'] instanceof $ ) {
var menuLeftEdge = this.$menu.offset().left + this.$menu.outerWidth();
var withinLeftEdge = this.settings['positionMenuWithin'].offset().left +
this.settings['positionMenuWithin'].outerWidth();
if ( menuLeftEdge > withinLeftEdge ) {
this.$menu.css( 'width', (withinLeftEdge - this.$menu.offset().left) );
this.$container.addClass(this.settings['positionedMenuClass']);
}
}
var menuBottom = this.$menu.offset().top + this.$menu.outerHeight();
var viewportBottom = $(window).scrollTop() + $(window).height();
if ( menuBottom > viewportBottom - this.settings['viewportBottomGutter'] ) {
this.$menu.css({
'maxHeight': Math.max(
viewportBottom - this.settings['viewportBottomGutter'] - this.$menu.offset().top,
this.settings['menuMinHeight']
),
'overflow': 'scroll'
});
} else {
this.$menu.css({
'maxHeight': '',
'overflow': ''
});
}
},
menuHide: function() {
this.$container.removeClass(this.settings['activeClass']);
this.$container.removeClass(this.settings['positionedMenuClass']);
this.$menu.css('width', 'auto');
},
menuToggle: function() {
if ( this.$container.hasClass(this.settings['activeClass']) ) {
this.menuHide();
} else {
this.menuShow();
}
}
});
$.fn[ pluginName ] = function(options) {
return this.each(function() {
if ( !$.data(this, "plugin_" + pluginName) ) {
$.data(this, "plugin_" + pluginName,
new MultiSelect(this, options) );
}
});
};
})(jQuery);

View File

@ -0,0 +1,9 @@
(function(c){function f(b,a){this.b=c(b);this.a=c.extend({},g,a);this.H()}var g={containerHTML:'<div class="multi-select-container">',menuHTML:'<div class="multi-select-menu">',buttonHTML:'<span class="multi-select-button">',menuItemsHTML:'<div class="multi-select-menuitems">',menuItemHTML:'<label class="multi-select-menuitem">',presetsHTML:'<div class="multi-select-presets">',activeClass:"multi-select-container--open",noneText:"-- Select --",allText:void 0,presets:void 0,positionedMenuClass:"multi-select-container--positioned",
positionMenuWithin:void 0,viewportBottomGutter:20,menuMinHeight:200};c.extend(f.prototype,{H:function(){this.v();this.G();this.A();this.w();this.B();this.J();this.K();this.b.hide()},v:function(){if(!1===this.b.is("select[multiple]"))throw Error("$.multiSelect only works on <select multiple> elements");},G:function(){this.l=c('label[for="'+this.b.attr("id")+'"]')},A:function(){this.f=c(this.a.containerHTML);this.b.data("multi-select-container",this.f);this.f.insertAfter(this.b)},w:function(){var b=
this;this.g=c(this.a.buttonHTML);this.g.attr({role:"button","aria-haspopup":"true",tabindex:0,"aria-label":this.l.eq(0).text()}).on("keydown.multiselect",function(a){a=a.which;13!==a&&32!==a||b.g.click()}).on("click.multiselect",function(){b.m()}).appendTo(this.f);this.b.on("change.multiselect",function(){b.o()});this.o()},o:function(){var b=[],a=[];this.b.children("option").each(function(){var d=c(this).text();b.push(d);c(this).is(":selected")&&a.push(c.trim(d))});this.g.empty();0==a.length?this.g.text(this.a.noneText):
a.length===b.length&&this.a.allText?this.g.text(this.a.allText):this.g.text(a.join(", "))},B:function(){var b=this;this.c=c(this.a.menuHTML);this.c.attr({role:"menu"}).on("keyup.multiselect",function(a){27===a.which&&b.j()}).appendTo(this.f);this.D();this.a.presets&&this.F()},D:function(){var b=this;this.h=c(this.a.menuItemsHTML);this.c.append(this.h);this.b.on("change.multiselect",function(a,c){!0!==c&&b.s()});this.s()},s:function(){var b=this;this.h.empty();this.b.children("option").each(function(a,
d){a=b.C(c(d),a);b.h.append(a)})},F:function(){var b=this;this.i=c(this.a.presetsHTML);this.c.prepend(this.i);c.each(this.a.presets,function(a,d){a=b.b.attr("name")+"_preset_"+a;var h=c(b.a.menuItemHTML).attr({"for":a,role:"menuitem"}).text(" "+d.name).appendTo(b.i);c("<input>").attr({type:"radio",name:b.b.attr("name")+"_presets",id:a}).prependTo(h).on("change.multiselect",function(){b.b.val(d.options);b.b.trigger("change")})});this.b.on("change.multiselect",function(){b.u()});this.u()},u:function(){var b=
this;c.each(this.a.presets,function(a,c){a=b.b.attr("name")+"_preset_"+a;a=b.i.find("#"+a);a:{c=c.options||[];var d=b.b.val()||[];if(c.length!=d.length)c=!1;else{c.sort();d.sort();for(var e=0;e<c.length;e++)if(c[e]!==d[e]){c=!1;break a}c=!0}}c?a.prop("checked",!0):a.prop("checked",!1)})},C:function(b,a){var d=this.b.attr("name")+"_"+a;a=c(this.a.menuItemHTML).attr({"for":d,role:"menuitem"}).text(" "+b.text());d=c("<input>").attr({type:"checkbox",id:d,value:b.val()}).prependTo(a);b.is(":disabled")&&
d.attr("disabled","disabled");b.is(":selected")&&d.prop("checked","checked");d.on("change.multiselect",function(){c(this).prop("checked")?b.prop("selected",!0):b.prop("selected",!1);b.trigger("change",[!0])});return a},J:function(){var b=this;c("html").on("click.multiselect",function(){b.j()});this.f.on("click.multiselect",function(a){a.stopPropagation()})},K:function(){var b=this;this.l.on("click.multiselect",function(a){a.preventDefault();a.stopPropagation();b.m()})},I:function(){c("html").trigger("click.multiselect");
this.f.addClass(this.a.activeClass);if(this.a.positionMenuWithin&&this.a.positionMenuWithin instanceof c){var b=this.c.offset().left+this.c.outerWidth(),a=this.a.positionMenuWithin.offset().left+this.a.positionMenuWithin.outerWidth();b>a&&(this.c.css("width",a-this.c.offset().left),this.f.addClass(this.a.positionedMenuClass))}b=this.c.offset().top+this.c.outerHeight();a=c(window).scrollTop()+c(window).height();b>a-this.a.viewportBottomGutter?this.c.css({maxHeight:Math.max(a-this.a.viewportBottomGutter-
this.c.offset().top,this.a.menuMinHeight),overflow:"scroll"}):this.c.css({maxHeight:"",overflow:""})},j:function(){this.f.removeClass(this.a.activeClass);this.f.removeClass(this.a.positionedMenuClass);this.c.css("width","auto")},m:function(){this.f.hasClass(this.a.activeClass)?this.j():this.I()}});c.fn.multiSelect=function(b){return this.each(function(){c.data(this,"plugin_multiSelect")||c.data(this,"plugin_multiSelect",new f(this,b))})}})(jQuery);

View File

@ -1,336 +0,0 @@
/*
* jQuery UI Multiselect
*
* Authors:
* Michael Aufreiter (quasipartikel.at)
* Yanick Rochon (yanick.rochon[at]gmail[dot]com)
*
* Dual licensed under the MIT (MIT-LICENSE.txt)
* and GPL (GPL-LICENSE.txt) licenses.
*
* http://www.quasipartikel.at/multiselect/
*
*
* Depends:
* ui.core.js
* ui.sortable.js
*
* Optional:
* localization (http://plugins.jquery.com/project/localisation)
* scrollTo (http://plugins.jquery.com/project/ScrollTo)
*
* Todo:
* Make batch actions faster
* Implement dynamic insertion through remote calls
*/
(function($) {
$.widget("ui.multiselect", {
options: {
sortable: true,
searchable: true,
doubleClickable: true,
animated: 'fast',
show: 'slideDown',
hide: 'slideUp',
dividerLocation: 0.6,
nodeComparator: function(node1,node2) {
var text1 = node1.text(),
text2 = node2.text();
return text1 == text2 ? 0 : (text1 < text2 ? -1 : 1);
}
},
_create: function() {
this.element.hide();
this.id = this.element.attr("id");
this.container = $('<div class="ui-multiselect ui-helper-clearfix ui-widget"></div>').insertAfter(this.element);
this.count = 0; // number of currently selected options
this.selectedContainer = $('<div class="selected"></div>').appendTo(this.container);
this.availableContainer = $('<div class="available"></div>').appendTo(this.container);
this.selectedActions = $('<div class="actions ui-widget-header ui-helper-clearfix"><span class="count">0 '+$.ui.multiselect.locale.itemsCount+'</span><a href="#" class="remove-all">'+$.ui.multiselect.locale.removeAll+'</a></div>').appendTo(this.selectedContainer);
this.availableActions = $('<div class="actions ui-widget-header ui-helper-clearfix"><input type="text" class="search empty ui-widget-content ui-corner-all"/><a href="#" class="add-all">'+$.ui.multiselect.locale.addAll+'</a></div>').appendTo(this.availableContainer);
this.selectedList = $('<ul class="selected connected-list"><li class="ui-helper-hidden-accessible"></li></ul>').bind('selectstart', function(){return false;}).appendTo(this.selectedContainer);
this.availableList = $('<ul class="available connected-list"><li class="ui-helper-hidden-accessible"></li></ul>').bind('selectstart', function(){return false;}).appendTo(this.availableContainer);
var that = this;
// set dimensions
this.container.width(this.element.width()+1);
this.selectedContainer.width(Math.floor(this.element.width()*this.options.dividerLocation));
this.availableContainer.width(Math.floor(this.element.width()*(1-this.options.dividerLocation)));
// fix list height to match <option> depending on their individual header's heights
this.selectedList.height(Math.max(this.element.height()-this.selectedActions.height(),1));
this.availableList.height(Math.max(this.element.height()-this.availableActions.height(),1));
if ( !this.options.animated ) {
this.options.show = 'show';
this.options.hide = 'hide';
}
// init lists
this._populateLists(this.element.find('option'));
// make selection sortable
if (this.options.sortable) {
this.selectedList.sortable({
placeholder: 'ui-state-highlight',
axis: 'y',
update: function(event, ui) {
// apply the new sort order to the original selectbox
that.selectedList.find('li').each(function() {
if ($(this).data('optionLink'))
$(this).data('optionLink').remove().appendTo(that.element);
});
},
receive: function(event, ui) {
ui.item.data('optionLink').attr('selected', true);
// increment count
that.count += 1;
that._updateCount();
// workaround, because there's no way to reference
// the new element, see http://dev.jqueryui.com/ticket/4303
that.selectedList.children('.ui-draggable').each(function() {
$(this).removeClass('ui-draggable');
$(this).data('optionLink', ui.item.data('optionLink'));
$(this).data('idx', ui.item.data('idx'));
that._applyItemState($(this), true);
});
// workaround according to http://dev.jqueryui.com/ticket/4088
setTimeout(function() { ui.item.remove(); }, 1);
}
});
}
// set up livesearch
if (this.options.searchable) {
this._registerSearchEvents(this.availableContainer.find('input.search'));
} else {
$('.search').hide();
}
// batch actions
this.container.find(".remove-all").click(function() {
that._populateLists(that.element.find('option').removeAttr('selected'));
return false;
});
this.container.find(".add-all").click(function() {
var options = that.element.find('option').not(":selected");
if (that.availableList.children('li:hidden').length > 1) {
that.availableList.children('li').each(function(i) {
if ($(this).is(":visible")) $(options[i-1]).attr('selected', 'selected');
});
} else {
options.attr('selected', 'selected');
}
that._populateLists(that.element.find('option'));
return false;
});
},
destroy: function() {
this.element.show();
this.container.remove();
$.Widget.prototype.destroy.apply(this, arguments);
},
_populateLists: function(options) {
this.selectedList.children('.ui-element').remove();
this.availableList.children('.ui-element').remove();
this.count = 0;
var that = this;
var items = $(options.map(function(i) {
var item = that._getOptionNode(this).appendTo(this.selected ? that.selectedList : that.availableList).show();
if (this.selected) that.count += 1;
that._applyItemState(item, this.selected);
item.data('idx', i);
return item[0];
}));
// update count
this._updateCount();
that._filter.apply(this.availableContainer.find('input.search'), [that.availableList]);
},
_updateCount: function() {
this.selectedContainer.find('span.count').text(this.count+" "+$.ui.multiselect.locale.itemsCount);
},
_getOptionNode: function(option) {
option = $(option);
var node = $('<li class="ui-state-default ui-element" title="'+option.text()+'"><span class="ui-icon"/>'+option.text()+'<a href="#" class="action"><span class="ui-corner-all ui-icon"/></a></li>').hide();
node.data('optionLink', option);
return node;
},
// clones an item with associated data
// didn't find a smarter away around this
_cloneWithData: function(clonee) {
var clone = clonee.clone(false,false);
clone.data('optionLink', clonee.data('optionLink'));
clone.data('idx', clonee.data('idx'));
return clone;
},
_setSelected: function(item, selected) {
item.data('optionLink').attr('selected', selected);
if (selected) {
var selectedItem = this._cloneWithData(item);
item[this.options.hide](this.options.animated, function() { $(this).remove(); });
selectedItem.appendTo(this.selectedList).hide()[this.options.show](this.options.animated);
this._applyItemState(selectedItem, true);
return selectedItem;
} else {
// look for successor based on initial option index
var items = this.availableList.find('li'), comparator = this.options.nodeComparator;
var succ = null, i = item.data('idx'), direction = comparator(item, $(items[i]));
// TODO: test needed for dynamic list populating
if ( direction ) {
while (i>=0 && i<items.length) {
direction > 0 ? i++ : i--;
if ( direction != comparator(item, $(items[i])) ) {
// going up, go back one item down, otherwise leave as is
succ = items[direction > 0 ? i : i+1];
break;
}
}
} else {
succ = items[i];
}
var availableItem = this._cloneWithData(item);
succ ? availableItem.insertBefore($(succ)) : availableItem.appendTo(this.availableList);
item[this.options.hide](this.options.animated, function() { $(this).remove(); });
availableItem.hide()[this.options.show](this.options.animated);
this._applyItemState(availableItem, false);
return availableItem;
}
},
_applyItemState: function(item, selected) {
if (selected) {
if (this.options.sortable)
item.children('span').addClass('ui-icon-arrowthick-2-n-s').removeClass('ui-helper-hidden').addClass('ui-icon');
else
item.children('span').removeClass('ui-icon-arrowthick-2-n-s').addClass('ui-helper-hidden').removeClass('ui-icon');
item.find('a.action span').addClass('ui-icon-minus').removeClass('ui-icon-plus');
this._registerRemoveEvents(item.find('a.action'));
} else {
item.children('span').removeClass('ui-icon-arrowthick-2-n-s').addClass('ui-helper-hidden').removeClass('ui-icon');
item.find('a.action span').addClass('ui-icon-plus').removeClass('ui-icon-minus');
this._registerAddEvents(item.find('a.action'));
}
this._registerDoubleClickEvents(item);
this._registerHoverEvents(item);
},
// taken from John Resig's liveUpdate script
_filter: function(list) {
var input = $(this);
var rows = list.children('li'),
cache = rows.map(function(){
return $(this).text().toLowerCase();
});
var term = $.trim(input.val().toLowerCase()), scores = [];
if (!term) {
rows.show();
} else {
rows.hide();
cache.each(function(i) {
if (this.indexOf(term)>-1) { scores.push(i); }
});
$.each(scores, function() {
$(rows[this]).show();
});
}
},
_registerDoubleClickEvents: function(elements) {
if (!this.options.doubleClickable) return;
elements.dblclick(function() {
elements.find('a.action').click();
});
},
_registerHoverEvents: function(elements) {
elements.removeClass('ui-state-hover');
elements.mouseover(function() {
$(this).addClass('ui-state-hover');
});
elements.mouseout(function() {
$(this).removeClass('ui-state-hover');
});
},
_registerAddEvents: function(elements) {
var that = this;
elements.click(function() {
var item = that._setSelected($(this).parent(), true);
that.count += 1;
that._updateCount();
return false;
});
// make draggable
if (this.options.sortable) {
elements.each(function() {
$(this).parent().draggable({
connectToSortable: that.selectedList,
helper: function() {
var selectedItem = that._cloneWithData($(this)).width($(this).width() - 50);
selectedItem.width($(this).width());
return selectedItem;
},
appendTo: that.container,
containment: that.container,
revert: 'invalid'
});
});
}
},
_registerRemoveEvents: function(elements) {
var that = this;
elements.click(function() {
that._setSelected($(this).parent(), false);
that.count -= 1;
that._updateCount();
return false;
});
},
_registerSearchEvents: function(input) {
var that = this;
input.focus(function() {
$(this).addClass('ui-state-active');
})
.blur(function() {
$(this).removeClass('ui-state-active');
})
.keypress(function(e) {
if (e.keyCode == 13)
return false;
})
.keyup(function() {
that._filter.apply(this, [that.availableList]);
});
}
});
$.extend($.ui.multiselect, {
locale: {
addAll:'Add all',
removeAll:'Remove all',
itemsCount:'items selected'
}
});
})(jQuery);

View File

@ -1296,7 +1296,11 @@ function top_htmlhead($head, $title='', $disablejs=0, $disablehead=0, $arrayofjs
$tmpplugin=empty($conf->global->MAIN_USE_JQUERY_MULTISELECT)?constant('REQUIRE_JQUERY_MULTISELECT'):$conf->global->MAIN_USE_JQUERY_MULTISELECT;
print '<script type="text/javascript" src="'.DOL_URL_ROOT.'/includes/jquery/plugins/'.$tmpplugin.'/dist/js/'.$tmpplugin.'.full.min.js'.($ext?'?'.$ext:'').'"></script>'."\n"; // We include full because we need the support of containerCssClass
}
}
if (! defined('DISABLE_MULTISELECT')) // jQuery plugin "mutiselect" to select with checkboxes
{
print '<script type="text/javascript" src="'.DOL_URL_ROOT.'/includes/jquery/plugins/multiselect/jquery.multi-select.js'.($ext?'?'.$ext:'').'"></script>'."\n";
}
}
if (! $disablejs && ! empty($conf->use_javascript_ajax))
{

View File

@ -87,8 +87,7 @@ $search_type_thirdparty=GETPOST("search_type_thirdparty",'int');
$search_staff=GETPOST("search_staff",'int');
$search_status=GETPOST("search_status",'int');
$search_type=GETPOST('search_type','alpha');
$search_level_from = GETPOST("search_level_from","alpha");
$search_level_to = GETPOST("search_level_to","alpha");
$search_level = GETPOST("search_level", "array");
$search_stcomm=GETPOST('search_stcomm','int');
$search_import_key = GETPOST("search_import_key","alpha");
$search_btn=GETPOST('button_search','alpha');
@ -263,8 +262,7 @@ if (empty($reshook))
$search_staff='';
$search_status=-1;
$search_stcomm='';
$search_level_from='';
$search_level_to='';
$search_level='';
$search_import_key='';
$toselect='';
$search_array_options=array();
@ -321,80 +319,22 @@ if ($type == 'c' && (empty($search_type) || ($search_type == '1,3'))) $title=$la
if ($type == 'p' && (empty($search_type) || ($search_type == '2,3'))) $title=$langs->trans("ListOfProspects");
if ($type == 'f' && (empty($search_type) || ($search_type == '4'))) $title=$langs->trans("ListOfSuppliers");
// If both parameters are set, search for everything BETWEEN them
if ($search_level_from != '' && $search_level_to != '')
{
// Ensure that these parameters are numbers
$search_level_from = (int) $search_level_from;
$search_level_to = (int) $search_level_to;
// If from is greater than to, reverse orders
if ($search_level_from > $search_level_to)
{
$tmp = $search_level_to;
$search_level_to = $search_level_from;
$search_level_from = $tmp;
}
// Generate the SQL request
$sortwhere = '(sortorder BETWEEN '.$search_level_from.' AND '.$search_level_to.') AS is_in_range';
}
// If only "from" parameter is set, search for everything GREATER THAN it
else if ($search_level_from != '')
{
// Ensure that this parameter is a number
$search_level_from = (int) $search_level_from;
// Generate the SQL request
$sortwhere = '(sortorder >= '.$search_level_from.') AS is_in_range';
}
// If only "to" parameter is set, search for everything LOWER THAN it
else if ($search_level_to != '')
{
// Ensure that this parameter is a number
$search_level_to = (int) $search_level_to;
// Generate the SQL request
$sortwhere = '(sortorder <= '.$search_level_to.') AS is_in_range';
}
// If no parameters are set, dont search for anything
else
{
$sortwhere = '0 as is_in_range';
}
// Select every potentiels, and note each potentiels which fit in search parameters
dol_syslog('societe/list.php',LOG_DEBUG);
$sql = "SELECT code, label, sortorder, ".$sortwhere;
$tab_level = array();
$sql = "SELECT code, label, sortorder";
$sql.= " FROM ".MAIN_DB_PREFIX."c_prospectlevel";
$sql.= " WHERE active > 0";
$sql.= " ORDER BY sortorder";
$resql = $db->query($sql);
if ($resql)
{
$tab_level = array();
$search_levels = array();
while ($obj = $db->fetch_object($resql))
{
// Compute level text
$level=$langs->trans($obj->code);
if ($level == $obj->code) $level=$langs->trans($obj->label);
// Put it in the array sorted by sortorder
$tab_level[$obj->sortorder] = $level;
// If this potentiel fit in parameters, add its code to the $search_levels array
if ($obj->is_in_range == 1)
{
$search_levels[] = '"'.preg_replace('[^A-Za-z0-9_-]', '', $obj->code).'"';
}
$tab_level[$obj->code] = $level;
}
// Implode the $search_levels array so that it can be use in a "IN (...)" where clause.
// If no paramters was set, $search_levels will be empty
$search_levels = implode(',', $search_levels);
}
else dol_print_error($db);
@ -474,12 +414,12 @@ if (strlen($search_vat)) $sql.= natural_search("s.tva_intra",$search_vat);
if ($search_type > 0 && in_array($search_type,array('1,3','2,3'))) $sql .= " AND s.client IN (".$db->escape($search_type).")";
if ($search_type > 0 && in_array($search_type,array('4'))) $sql .= " AND s.fournisseur = 1";
if ($search_type == '0') $sql .= " AND s.client = 0 AND s.fournisseur = 0";
if ($search_status!='' && $search_status >= 0) $sql .= " AND s.status = ".$db->escape($search_status);
if ($search_status!='' && $search_status >= 0) $sql .= natural_search("s.status", $search_status, 2);
if (!empty($conf->barcode->enabled) && $search_barcode) $sql.= natural_search("s.barcode", $search_barcode);
if ($search_type_thirdparty && $search_type_thirdparty != '-1') $sql.= " AND s.fk_typent IN (".$db->escape($search_type_thirdparty).')';
if (! empty($search_staff) && $search_staff != '-1') $sql.= " AND s.fk_effectif IN (".$db->escape($search_staff).")";
if ($search_levels) $sql .= " AND s.fk_prospectlevel IN (".$search_levels.')';
if ($search_stcomm != '' && $search_stcomm != -2) $sql.= natural_search("s.fk_stcomm",$search_stcomm,2);
if ($search_type_thirdparty && $search_type_thirdparty != '-1') $sql.= natural_search("s.fk_typent", $search_type_thirdparty, 2);
if (! empty($search_staff) && $search_staff != '-1') $sql.= natural_search("s.fk_effectif", $search_staff, 2);
if ($search_level) $sql .= natural_search("s.fk_prospectlevel", join(',', $search_level), 3);
if ($search_stcomm != '' && $search_stcomm != -2) $sql.= natural_search("s.fk_stcomm", $search_stcomm, 2);
if ($search_import_key) $sql.= natural_search("s.import_key",$search_import_key);
// Add where from extra fields
include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_sql.tpl.php';
@ -572,13 +512,12 @@ if ($search_idprof6 != '') $param.= '&search_idprof6='.urlencode($search_idprof6
if ($search_vat != '') $param.= '&search_vat='.urlencode($search_vat);
if ($search_type_thirdparty != '') $param.='&search_type_thirdparty='.urlencode($search_type_thirdparty);
if ($search_type != '') $param.='&search_type='.urlencode($search_type);
if ($optioncss != '') $param.='&optioncss='.urlencode($optioncss);
if (is_array($search_level) && count($search_level)) foreach($search_level as $slevel) $param.='&search_level[]='.urlencode($slevel);
if ($search_status != '') $param.='&search_status='.urlencode($search_status);
if ($search_stcomm != '') $param.='&search_stcomm='.urlencode($search_stcomm);
if ($search_level_from != '') $param.='&search_level_from='.urlencode($search_level_from);
if ($search_level_to != '') $param.='&search_level_to='.urlencode($search_level_to);
if ($search_import_key != '') $param.='&search_import_key='.urlencode($search_import_key);
if ($type != '') $param.='&type='.urlencode($type);
if ($optioncss != '') $param.='&optioncss='.urlencode($optioncss);
// Add $param from extra fields
include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_param.tpl.php';
@ -901,37 +840,16 @@ if (! empty($arrayfields['customerorsupplier']['checked']))
print '<option value="0"'.($search_type=='0'?' selected':'').'>'.$langs->trans('Others').'</option>';
print '</select></td>';
}
// Prospect level
if (! empty($arrayfields['s.fk_prospectlevel']['checked']))
{
// Prospect level
print '<td class="liste_titre" align="center">';
$options_from = '<option value="">&nbsp;</option>'; // Generate in $options_from the list of each option sorted
foreach ($tab_level as $tab_level_sortorder => $tab_level_label)
{
$options_from .= '<option value="'.$tab_level_sortorder.'"'.($search_level_from == $tab_level_sortorder ? ' selected':'').'>';
$options_from .= $langs->trans($tab_level_label);
$options_from .= '</option>';
}
array_reverse($tab_level, true); // Reverse the list
$options_to = '<option value="">&nbsp;</option>'; // Generate in $options_to the list of each option sorted in the reversed order
foreach ($tab_level as $tab_level_sortorder => $tab_level_label)
{
$options_to .= '<option value="'.$tab_level_sortorder.'"'.($search_level_to == $tab_level_sortorder ? ' selected':'').'>';
$options_to .= $langs->trans($tab_level_label);
$options_to .= '</option>';
}
// Print these two select
print $langs->trans("From").' <select class="flat" name="search_level_from">'.$options_from.'</select>';
print ' ';
print $langs->trans("to").' <select class="flat" name="search_level_to">'.$options_to.'</select>';
print $form->multiselectarray('search_level', $tab_level, $search_level, 0, 0, 'width75', 0, 0, '', '', '', 2);
print '</td>';
}
// Prospect status
if (! empty($arrayfields['s.fk_stcomm']['checked']))
{
// Prospect status
print '<td class="liste_titre maxwidthonsmartphone" align="center">';
$arraystcomm=array();
foreach($prospectstatic->cacheprospectstatus as $key => $val)
@ -963,7 +881,7 @@ if (! empty($arrayfields['s.tms']['checked']))
// Status
if (! empty($arrayfields['s.status']['checked']))
{
print '<td class="liste_titre maxwidthonsmartphone center">';
print '<td class="liste_titre center minwidth75imp">';
print $form->selectarray('search_status', array('0'=>$langs->trans('ActivityCeased'),'1'=>$langs->trans('InActivity')), $search_status, 1);
print '</td>';
}

View File

@ -2718,7 +2718,9 @@ table.paddingtopbottomonly tr td {
background: rgb(<?php echo $colorbacktitle1; ?>) !important;
}
tr.liste_titre_filter td.liste_titre {
/* border-bottom: 1px solid #ddd; */
/* border-bottom: 1px solid #ddd; */
padding-top: 2px;
padding-bottom: 2px;
}
.liste_titre_create td, .liste_titre_create th, .liste_titre_create .tagtd
{
@ -4913,7 +4915,81 @@ span.noborderoncategories {
/* ============================================================================== */
/* Multiselect with checkbox */
/* External lib multiselect with checkbox */
/* ============================================================================== */
.multi-select-container {
display: inline-block;
position: relative;
}
.multi-select-menu {
position: absolute;
left: 0;
top: 0.8em;
float: left;
min-width: 100%;
background: #fff;
margin: 1em 0;
padding: 0.4em 0;
border: 1px solid #aaa;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
display: none;
}
.multi-select-menu input {
margin-right: 0.3em;
vertical-align: 0.1em;
}
.multi-select-button {
display: inline-block;
max-width: 20em;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
vertical-align: middle;
background-color: #fff;
cursor: default;
border: none;
border-bottom: solid 1px rgba(0,0,0,.2);
padding: 5px;
padding-left: 2px;
height: 17px;
}
.multi-select-button:focus {
outline: none;
border-bottom: 1px solid #666;
}
.multi-select-button:after {
content: "";
display: inline-block;
width: 0;
height: 0;
border-style: solid;
border-width: 0.5em 0.23em 0em 0.23em;
border-color: #444 transparent transparent transparent;
margin-left: 0.4em;
}
.multi-select-container--open .multi-select-menu { display: block; }
.multi-select-container--open .multi-select-button:after {
border-width: 0 0.4em 0.4em 0.4em;
border-color: transparent transparent #999 transparent;
}
.multi-select-menuitem {
clear: both;
float: left;
padding-left: 5px
}
/* ============================================================================== */
/* Native multiselect with checkbox */
/* ============================================================================== */
ul.ulselectedfields {

View File

@ -2685,6 +2685,8 @@ table.paddingtopbottomonly tr td {
}
tr.liste_titre_filter td.liste_titre {
border-bottom: 1px solid #FDFFFF;
padding-top: 2px;
padding-bottom: 2px;
}
.liste_titre_create td, .liste_titre_create th, .liste_titre_create .tagtd
{
@ -4772,7 +4774,81 @@ span.noborderoncategories {
/* ============================================================================== */
/* Multiselect with checkbox */
/* External lib multiselect with checkbox */
/* ============================================================================== */
.multi-select-container {
display: inline-block;
position: relative;
}
.multi-select-menu {
position: absolute;
left: 0;
top: 0.8em;
float: left;
min-width: 100%;
background: #fff;
margin: 1em 0;
padding: 0.4em 0;
border: 1px solid #aaa;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
display: none;
}
.multi-select-menu input {
margin-right: 0.3em;
vertical-align: 0.1em;
}
.multi-select-button {
display: inline-block;
max-width: 20em;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
vertical-align: middle;
background-color: #fff;
cursor: default;
border: none;
border-bottom: solid 1px rgba(0,0,0,.2);
padding: 5px;
padding-left: 2px;
height: 17px;
}
.multi-select-button:focus {
outline: none;
border-bottom: 1px solid #666;
}
.multi-select-button:after {
content: "";
display: inline-block;
width: 0;
height: 0;
border-style: solid;
border-width: 0.5em 0.23em 0em 0.23em;
border-color: #444 transparent transparent transparent;
margin-left: 0.4em;
}
.multi-select-container--open .multi-select-menu { display: block; }
.multi-select-container--open .multi-select-button:after {
border-width: 0 0.4em 0.4em 0.4em;
border-color: transparent transparent #999 transparent;
}
.multi-select-menuitem {
clear: both;
float: left;
padding-left: 5px
}
/* ============================================================================== */
/* Native multiselect with checkbox */
/* ============================================================================== */
ul.ulselectedfields {