From 5f83af2fa1c71b2f27816d867a8c9c61531d83ee Mon Sep 17 00:00:00 2001 From: larsz Date: Mon, 30 Jan 2012 01:06:15 +0200 Subject: [PATCH] added styles for hover on mass operation checkbox, fixed incorrect blink behavior (issue #41) --- web/css/main.css | 28 +- web/images/checkbox-selector-2012-01-29.png | Bin 0 -> 1276 bytes web/index.html | 12 +- web/js/lib/jquery.ui.widget.js | 236 +++++++++++++++++ web/js/lib/jquery.usermode.js | 75 ++++++ web/js/lib/ui.checkbox.js | 276 ++++++++++++++++++++ web/js/ui.checkbox.init.js | 3 + 7 files changed, 618 insertions(+), 12 deletions(-) create mode 100644 web/images/checkbox-selector-2012-01-29.png create mode 100644 web/js/lib/jquery.ui.widget.js create mode 100644 web/js/lib/jquery.usermode.js create mode 100644 web/js/lib/ui.checkbox.js create mode 100644 web/js/ui.checkbox.init.js diff --git a/web/css/main.css b/web/css/main.css index 5a6a1026a..602e3474c 100644 --- a/web/css/main.css +++ b/web/css/main.css @@ -3,7 +3,7 @@ Title : Vesta Author : Eugen Lobicov, eugen.lobicov@gmail.com created : November 27, 2009 -last updated : January 26, 2012 +last updated : January 30, 2012 - - - - - - - - - - - - - - - - - - */ html{ @@ -578,24 +578,26 @@ input::-moz-focus-inner{ } .checkbox-selector, -.context-actions{ +.context-actions, +.checkbox-selector .ui-checkbox{ display:-moz-inline-stack; display: inline-block; zoom: 1; *display: inline; vertical-align:top; } - .checkbox-selector .checkbox{ - display:block; - float:left; + .ui-helper-hidden-accessible{ + display:none; + } + .checkbox-selector span.ui-checkbox{ + float:none; width:11px; height:11px; - margin:2px 5px 0 0; - background:url(../images/checkbox-selector.png) no-repeat; + margin:2px 2px 0 0; + background:url(../images/checkbox-selector-2012-01-29.png) no-repeat; cursor:pointer; } .checkbox-selector .selector-title{ - float:left; font-size:11px; line-height:15px; text-transform:uppercase; @@ -603,8 +605,14 @@ input::-moz-focus-inner{ cursor:pointer; white-space:nowrap; } - .checkbox-selector:hover .checkbox{ - background-position:0 -90px; + .checkbox-selector span.ui-checkbox-hover{ + background-position:0 -20px; + } + .checkbox-selector span.ui-checkbox-state-checked{ + background-position:0 -40px; + } + .checkbox-selector span.ui-checkbox-state-checked-hover{ + background-position:0 -60px; } .checkbox-selector .selector-title:hover{ color:#2ea8bd; diff --git a/web/images/checkbox-selector-2012-01-29.png b/web/images/checkbox-selector-2012-01-29.png new file mode 100644 index 0000000000000000000000000000000000000000..d5d708a5d4ccfbf572556dc3859efdf514107dfa GIT binary patch literal 1276 zcma)5ZD?Cn7`}DP)Ik*X%ldh_DWw&2KhlqzToOytBwfgEEJ?;Pf7ssMd)nN#_rrTn zm$X0Bsei;jYsGeifn{v#hD>x@9BdRzCuq^iaC5C`vWb-yHfx!nj14-^O`FaiMm#^x z7tiy&&-$ev&vt=2wVy93R02md^RmO2y;x(LvE85$&(w-Yka%6aWQ0s|=|om-P{4T@LN7Yl^_j z5IE=~Hch4YF1$lC0NzT{PLXb-@ivBJnp;?weHnMr3{BB4ig7s^mTPu#G>vZ!0!1^V zJ}w>%Z^c3nH*l~M)B^ZyN1lx?&L9BRZhs zz|@8f5j|X=!wRKy9R?7fW+XLjs8YqQv<5XZt?772q7~mStCE&6%V?hGBB}`mRRocs zk3f7ATk-Aij#y)CX(-Ah|lE``ic*0cWOnJZVQcdYGp)1t$6+XEH# zx`CS7w(mHJKE&a!y~&WxX4}}+1{4ORKB5ipApblT+`$ zcc{C&`+WYh@$vDgV@FFjmgnc^e=01_&dn?r3)9onCRE49#?D|clK z(1C{WSYq?$f6rL&i7Q#e%|(L#WVZ0#Sm*A`bZy9QMAv8BGY1YoxJfon{K`+f@ao2G z`?Ei{HwLD9CNF;e+TADR$N1W(--?w|XVQPM-SYWA_N`yLCVX(~hx_-w_tqC`$sTy; zE8Cr4ijHFmx$*fcSHHdZ`{Kh@`z&4%iS(uGFTON+{i@S^{BB%7`Fh>#&YcgQICT5X r{8TJGH#Ggt{Zr8c_bxAYzFmV&EDh(LdhX0_
- - None + +
@@ -251,6 +251,14 @@ + + + + + + + + diff --git a/web/js/lib/jquery.ui.widget.js b/web/js/lib/jquery.ui.widget.js new file mode 100644 index 000000000..b1f736bf7 --- /dev/null +++ b/web/js/lib/jquery.ui.widget.js @@ -0,0 +1,236 @@ +/*! + * jQuery UI Widget 1.8 + * + * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT (MIT-LICENSE.txt) + * and GPL (GPL-LICENSE.txt) licenses. + * + * http://docs.jquery.com/UI/Widget + */ +(function( $ ) { + +var _remove = $.fn.remove; + +$.fn.remove = function( selector, keepData ) { + return this.each(function() { + if ( !keepData ) { + if ( !selector || $.filter( selector, [ this ] ).length ) { + $( "*", this ).add( this ).each(function() { + $( this ).triggerHandler( "remove" ); + }); + } + } + return _remove.call( $(this), selector, keepData ); + }); +}; + +$.widget = function( name, base, prototype ) { + var namespace = name.split( "." )[ 0 ], + fullName; + name = name.split( "." )[ 1 ]; + fullName = namespace + "-" + name; + + if ( !prototype ) { + prototype = base; + base = $.Widget; + } + + // create selector for plugin + $.expr[ ":" ][ fullName ] = function( elem ) { + return !!$.data( elem, name ); + }; + + $[ namespace ] = $[ namespace ] || {}; + $[ namespace ][ name ] = function( options, element ) { + // allow instantiation without initializing for simple inheritance + if ( arguments.length ) { + this._createWidget( options, element ); + } + }; + + var basePrototype = new base(); + // we need to make the options hash a property directly on the new instance + // otherwise we'll modify the options hash on the prototype that we're + // inheriting from +// $.each( basePrototype, function( key, val ) { +// if ( $.isPlainObject(val) ) { +// basePrototype[ key ] = $.extend( {}, val ); +// } +// }); + basePrototype.options = $.extend( {}, basePrototype.options ); + $[ namespace ][ name ].prototype = $.extend( true, basePrototype, { + namespace: namespace, + widgetName: name, + widgetEventPrefix: $[ namespace ][ name ].prototype.widgetEventPrefix || name, + widgetBaseClass: fullName + }, prototype ); + + $.widget.bridge( name, $[ namespace ][ name ] ); +}; + +$.widget.bridge = function( name, object ) { + $.fn[ name ] = function( options ) { + var isMethodCall = typeof options === "string", + args = Array.prototype.slice.call( arguments, 1 ), + returnValue = this; + + // allow multiple hashes to be passed on init + options = !isMethodCall && args.length ? + $.extend.apply( null, [ true, options ].concat(args) ) : + options; + + // prevent calls to internal methods + if ( isMethodCall && options.substring( 0, 1 ) === "_" ) { + return returnValue; + } + + if ( isMethodCall ) { + this.each(function() { + var instance = $.data( this, name ), + methodValue = instance && $.isFunction( instance[options] ) ? + instance[ options ].apply( instance, args ) : + instance; + if ( methodValue !== instance && methodValue !== undefined ) { + returnValue = methodValue; + return false; + } + }); + } else { + this.each(function() { + var instance = $.data( this, name ); + if ( instance ) { + if ( options ) { + instance.option( options ); + } + instance._init(); + } else { + $.data( this, name, new object( options, this ) ); + } + }); + } + + return returnValue; + }; +}; + +$.Widget = function( options, element ) { + // allow instantiation without initializing for simple inheritance + if ( arguments.length ) { + this._createWidget( options, element ); + } +}; + +$.Widget.prototype = { + widgetName: "widget", + widgetEventPrefix: "", + options: { + disabled: false + }, + _createWidget: function( options, element ) { + // $.widget.bridge stores the plugin instance, but we do it anyway + // so that it's stored even before the _create function runs + this.element = $( element ).data( this.widgetName, this ); + this.options = $.extend( true, {}, + this.options, + $.metadata && $.metadata.get( element )[ this.widgetName ], + options ); + + var self = this; + this.element.bind( "remove." + this.widgetName, function() { + self.destroy(); + }); + + this._create(); + this._init(); + }, + _create: function() {}, + _init: function() {}, + + destroy: function() { + this.element + .unbind( "." + this.widgetName ) + .removeData( this.widgetName ); + this.widget() + .unbind( "." + this.widgetName ) + .removeAttr( "aria-disabled" ) + .removeClass( + this.widgetBaseClass + "-disabled " + + this.namespace + "-state-disabled" ); + }, + + widget: function() { + return this.element; + }, + + option: function( key, value ) { + var options = key, + self = this; + + if ( arguments.length === 0 ) { + // don't return a reference to the internal hash + return $.extend( {}, self.options ); + } + + if (typeof key === "string" ) { + if ( value === undefined ) { + return this.options[ key ]; + } + options = {}; + options[ key ] = value; + } + + $.each( options, function( key, value ) { + self._setOption( key, value ); + }); + + return self; + }, + _setOption: function( key, value ) { + this.options[ key ] = value; + + if ( key === "disabled" ) { + this.widget() + [ value ? "addClass" : "removeClass"]( + this.widgetBaseClass + "-disabled" + " " + + this.namespace + "-state-disabled" ) + .attr( "aria-disabled", value ); + } + + return this; + }, + + enable: function() { + return this._setOption( "disabled", false ); + }, + disable: function() { + return this._setOption( "disabled", true ); + }, + + _trigger: function( type, event, data ) { + var callback = this.options[ type ]; + + event = $.Event( event ); + event.type = ( type === this.widgetEventPrefix ? + type : + this.widgetEventPrefix + type ).toLowerCase(); + data = data || {}; + + // copy original event properties over to the new event + // this would happen if we could call $.event.fix instead of $.Event + // but we don't have a way to force an event to be fixed multiple times + if ( event.originalEvent ) { + for ( var i = $.event.props.length, prop; i; ) { + prop = $.event.props[ --i ]; + event[ prop ] = event.originalEvent[ prop ]; + } + } + + this.element.trigger( event, data ); + + return !( $.isFunction(callback) && + callback.call( this.element[0], event, data ) === false || + event.isDefaultPrevented() ); + } +}; + +})( jQuery ); diff --git a/web/js/lib/jquery.usermode.js b/web/js/lib/jquery.usermode.js new file mode 100644 index 000000000..9efc99cfe --- /dev/null +++ b/web/js/lib/jquery.usermode.js @@ -0,0 +1,75 @@ +/** + * @author trixta + */ +(function($){ + $.userMode = (function(){ + var userBg, + timer, + testDiv, + boundEvents = 0; + + function testBg(){ + testDiv = testDiv || $('
').css({position: 'absolute', left: '-999em', top: '-999px', width: '0px', height: '0px'}).appendTo('body'); + var black = $.curCSS( testDiv.css({backgroundColor: '#000000'})[0], 'backgroundColor', true), + white = $.curCSS( testDiv.css({backgroundColor: '#ffffff'})[0], 'backgroundColor', true), + newBgStatus = (black === white || white === 'transparent'); + if(newBgStatus != userBg){ + userBg = newBgStatus; + $.event.trigger('_internalusermode'); + } + return userBg; + } + + function init(){ + testBg(); + timer = setInterval(testBg, 3000); + } + + function stop(){ + clearInterval(timer); + testDiv.remove(); + testDiv = null; + } + + $.event.special.usermode = { + setup: function(){ + (!boundEvents && init()); + boundEvents++; + var jElem = $(this) + .bind('_internalusermode', $.event.special.usermode.handler); + //always trigger + setTimeout(function(){ + jElem.triggerHandler('_internalusermode'); + }, 1); + return true; + }, + teardown: function(){ + boundEvents--; + (!boundEvents && stop()); + $(this).unbind('_internalusermode', $.event.special.usermode.handler); + return true; + }, + handler: function(e){ + e.type = 'usermode'; + e.disabled = !userBg; + e.enabled = userBg; + return jQuery.event.handle.apply(this, arguments); + } + }; + + return { + get: testBg + }; + + })(); + + $.fn.userMode = function(fn){ + return this[(fn) ? 'bind' : 'trigger']('usermode', fn); + }; + + $(function(){ + $('html').userMode(function(e){ + $('html')[e.enabled ? 'addClass' : 'removeClass']('hcm'); + }); + }); +})(jQuery); diff --git a/web/js/lib/ui.checkbox.js b/web/js/lib/ui.checkbox.js new file mode 100644 index 000000000..fa050b81a --- /dev/null +++ b/web/js/lib/ui.checkbox.js @@ -0,0 +1,276 @@ +/** + * @author alexander.farkas + * @version 1.4.3 + */ +(function($){ + + var supportsValidity; + (function(){ + if(!$.prop || supportsValidity){return;} + var supportTest = function(){ + supportsValidity = !!$('').prop('validity'); + }; + supportTest(); + $(supportTest); + })(); + + $.widget('ui.checkBox', { + options: { + hideInput: true, + addVisualElement: true, + addLabel: true + }, + _create: function(){ + var that = this, + opts = this.options + ; + + if(!this.element.is(':radio,:checkbox')){ + if(this.element[0].elements && $.nodeName(this.element[0], 'form')){ + $(this.element[0].elements).filter(':radio,:checkbox').checkBox(opts); + } + return false; + } + + this._proxiedReflectUI = $.proxy(this, 'reflectUI'); + + this.labels = $([]); + + this.checkedStatus = false; + this.disabledStatus = false; + this.hoverStatus = false; + + this.inputType = this.element[0].type; + this.radio = this.inputType == 'radio'; + + this.visualElement = $([]); + if (opts.hideInput) { + this.element.addClass('ui-helper-hidden-accessible'); + if(opts.addVisualElement){ + this.visualElement = $('') + .addClass('ui-'+this.inputType) + ; + this.element.after(this.visualElement[0]); + } + } + + if(opts.addLabel){ + var id = this.element[0].id; + if(id){ + this.labels = $('label[for="' + id + '"]', this.element[0].form || this.element[0].ownerDocument).add(this.element.parent('label')); + } + if(!this.labels[0]){ + this.labels = this.element.closest('label', this.element[0].form); + } + this.labels.addClass(this.radio ? 'ui-radio' : 'ui-checkbox'); + } + + this.visualGroup = this.visualElement.add(this.labels); + + this._addEvents(); + + this.initialized = true; + this.reflectUI({type: 'initialreflect'}); + return undefined; + }, + _addEvents: function(){ + var that = this, + + opts = this.options, + + toggleHover = function(e){ + if(that.disabledStatus){ + return false; + } + that.hover = (e.type == 'focus' || e.type == 'mouseenter'); + if(e.type == 'focus'){ + that.visualGroup.addClass(that.inputType +'-focused'); + } else if(e.type == 'blur'){ + that.visualGroup.removeClass(that.inputType +'-focused'); + } + that._changeStateClassChain(); + return undefined; + } + ; + + this.element + .bind('click.checkBox invalid.checkBox', this._proxiedReflectUI) + .bind('focus.checkBox blur.checkBox', toggleHover) + ; + if (opts.hideInput){ + this.element + .bind('usermode', function(e){ + (e.enabled && + that.destroy.call(that, true)); + }) + ; + } + if(opts.addVisualElement){ + this.visualElement + .bind('click.checkBox', function(e){ + that.element[0].click(); + return false; + }) + ; + } + + this.visualGroup.bind('mouseenter.checkBox mouseleave.checkBox', toggleHover); + + }, + _changeStateClassChain: function(){ + var allElements = this.labels.add(this.visualElement), + stateClass = '', + baseClass = 'ui-'+ this.inputType + ; + + if(this.checkedStatus){ + stateClass += '-checked'; + allElements.addClass(baseClass+'-checked'); + } else { + allElements.removeClass(baseClass+'-checked'); + } + + if(this.disabledStatus){ + stateClass += '-disabled'; + allElements.addClass(baseClass+'-disabled'); + } else { + allElements.removeClass(baseClass+'-disabled'); + } + if(this.hover){ + stateClass += '-hover'; + allElements.addClass(baseClass+'-hover'); + } else { + allElements.removeClass(baseClass+'-hover'); + } + + baseClass += '-state'; + if(stateClass){ + stateClass = baseClass + stateClass; + } + + function switchStateClass(){ + var classes = this.className.split(' '), + found = false; + $.each(classes, function(i, classN){ + if(classN.indexOf(baseClass) === 0){ + found = true; + classes[i] = stateClass; + return false; + } + return undefined; + }); + if(!found){ + classes.push(stateClass); + } + this.className = classes.join(' '); + } + + this.visualGroup.each(switchStateClass); + }, + destroy: function(onlyCss){ + this.element.removeClass('ui-helper-hidden-accessible'); + this.visualElement.addClass('ui-helper-hidden'); + if (!onlyCss) { + var o = this.options; + this.element.unbind('.checkBox'); + this.visualElement.remove(); + this.labels + .unbind('.checkBox') + .removeClass('ui-state-hover ui-state-checked ui-state-disabled') + ; + } + }, + + disable: function(status){ + if(status === undefined){ + status = true; + } + this.element[0].disabled = status; + this.reflectUI({type: 'manuallydisabled'}); + }, + + enable: function(){ + this.element[0].disabled = false; + this.reflectUI({type: 'manuallyenabled'}); + }, + + toggle: function(e){ + this.changeCheckStatus(!(this.element.is(':checked')), e); + }, + + changeCheckStatus: function(status, e){ + if(e && e.type == 'click' && this.element[0].disabled){ + return false; + } + this.element[0].checked = !!status; + this.reflectUI(e || { + type: 'changecheckstatus' + }); + return undefined; + }, + propagate: function(n, e, _noGroupReflect){ + if(!e || e.type != 'initialreflect'){ + if (this.radio && !_noGroupReflect) { + var elem = this.element[0]; + //dynamic + $('[name="'+ elem.name +'"]', elem.form || elem.ownerDocument).checkBox('reflectUI', e, true); + + } + return this._trigger(n, e, { + options: this.options, + checked: this.checkedStatus, + labels: this.labels, + disabled: this.disabledStatus + }); + } + return undefined; + }, + changeValidityState: function(){ + if(supportsValidity){ + this.visualGroup[ !this.element.prop('willValidate') || (this.element.prop('validity') || {valid: true}).valid ? 'removeClass' : 'addClass' ](this.inputType +'-invalid'); + } + }, + reflectUI: function(e){ + + var oldChecked = this.checkedStatus, + oldDisabledStatus = this.disabledStatus + ; + + this.disabledStatus = this.element.is(':disabled'); + this.checkedStatus = this.element.is(':checked'); + if(!e || e.type !== 'initialreflect'){ + this.changeValidityState(); + } + + if (this.disabledStatus != oldDisabledStatus || this.checkedStatus !== oldChecked) { + this._changeStateClassChain(); + + (this.disabledStatus != oldDisabledStatus && + this.propagate('disabledchange', e)); + + (this.checkedStatus !== oldChecked && + this.propagate('change', e)); + } + + } + }); + + if($.propHooks){ + $.each({checked: 'changeCheckStatus', disabled: 'disable'}, function(name, fn){ + //be hook friendly + if(!$.propHooks[name]){ + $.propHooks[name] = {}; + } + var oldSetHook = $.propHooks[name].set; + + $.propHooks[name].set = function(elem, value){ + var widget = $.data(elem, 'checkBox'); + if(widget){ + widget[fn](!!value); + } + return oldSetHook && oldSetHook(elem, value) ; + }; + + }); + } +})(jQuery); diff --git a/web/js/ui.checkbox.init.js b/web/js/ui.checkbox.init.js new file mode 100644 index 000000000..cadef9a1b --- /dev/null +++ b/web/js/ui.checkbox.init.js @@ -0,0 +1,3 @@ +$(document).ready(function(){ + $('.cust-checkbox').checkBox(); +}); \ No newline at end of file