diff --git a/API.txt b/API.txt deleted file mode 100644 index b1b4771..0000000 --- a/API.txt +++ /dev/null @@ -1,32 +0,0 @@ -These are the methods, the plugin adds to the different namespaces. - -jQuery.fn - - sheet: returns the stylesheets from the matched styles and links. - - cssRules: return all the rules from all the given sheets. - - ownerNode: returns the nodes that belong to the given sheet (opposite to sheets). - - cssText: returns the text of the first matched style/link. - -jQuery.rule - - constructor( $.rule ): - - 1st argument: nothing, a rule filter, rule literal, css rule or array of rules. - - 2nd argument: nothing, node filter for link/style, nodes link/style. - - sheets: returns the sheets that match the selector or all of them if none. - - clean: converts a rule literal, to array of rules. - - parent: returns the parent of a rule, neccesary for IE. - - outerText: return the selector with the rules of the given rule. - - text: gets/sets the cssText of the rule. - -jQuery.rule.fn - - append: will add one or more styles in the form of "attr:value; attr:value" to the matched rules. - - css : sets a value to all matched rules. - - outerText: return $.rule.outerText of the first rule. - - text: sets the cssText of the rules, or gets the cssText from the first one. - - appendTo: appends the matched rules to the specified stylesheet(1), can be a selector, dom element, sheet. - -All these methods ( from jQuery.rule.fn ) are equal (or very similar) to those in jQuery.fn, but for CSS Rules. - add, andSelf, animate, appendTo, attr, css, dequeue, - each, end, eq, fadeIn, fadeOut, fadeTo, filter, get, - hide, index, is, map, not, pushStack, queue, remove, - setArray, show, size, slice, stop, toggle. - -Some calls to show and hide behave unexpectedly sometimes. Some styles and animations might fail, please report it. \ No newline at end of file diff --git a/changes.txt b/CHANGELOG.md similarity index 75% rename from changes.txt rename to CHANGELOG.md index f2fad6e..e8b42f0 100644 --- a/changes.txt +++ b/CHANGELOG.md @@ -1,39 +1,53 @@ -1.0.1 -[Fix] -- Small fix for Opera 9.55. The created demanded a closing slash. -- Fixed the exclusion of $.rule's sheet in $.rule.sheets(). Thanks to Adrien Gibrat for noticing. -- Literal rules were split by ','. That would break comma-separated selectors. Reported by Adrien Gibrat. - Note that IE doesn't support comma-separated selectors in the addRule method. This could be worked around but would - cause different results among browsers, so just now that it fails in IE and it should not be used. -[Optimization] -- Added a cleanup of the local storage on window.unload -[Docs] -- Added some notes to the source file (jquery.rule.js). - -1.0 -- Added semicolon to the start, for safe file concatenation -- Minor changes for perfomance -- Saved $.rule in a variable to make compressed code much shorter. -- Replaced the 'x:y' for IE when creating empty rules for ';'. -- Made many changes tp $.rule.fn.filter: - * The filtering function used to get element & index as arguments instead of element as 'this' and index as 1st argument. - * String filters are no longer turned into regex, now they are compared (case insensitive) to each (splitted by comma) selector. -- $.data and $.removeData have been extended ( hacked :) ). Instead of the element(rule), they get an empty hash, stored in $.rule.cache. -- The changes in $.data allow cross browser animations. They need some aggressive testing. -- Added show/hide/toggle and slide functions to $.rule.fn. -- Improved the hack to $.curCSS, it returns some default values in case none is set, for animations. It needs some more work. - -0.9.2 -- Added queue,dequeue,animate and stop to $.rule.fn (non-IE) -- Hacked $.curCSS to make the animations work. -- remove() works in Opera 9 and Safari Win! - - Opera needed 'alternate' in the rel of stylesheet - - Safari wanted the sheet to be disabled, not the node. -- $.rule.fn.filter now supports a regex as filter. - -0.9 -- Made the code work faster. -- Extended $.fn with ownerNode,sheet,cssRules and cssText. -- Added text and outerText to $.rule and $.rule.fn. -- Made the example nicer. +1.1.2 +[Fix] +- Changing rules property existence check for CORS + +1.1.1 +[Fix] +- Made compatible with jquery 3.0+. +- Added NPM manifest. + +1.1.0 +[Fix] +- Fix for FireFox "SecurityError: The operation is insecure." if an external CSS ref is present. Thanks for @dorival for the PR + +1.0.1 +[Fix] +- Small fix for Opera 9.55. The \').appendTo('head')[0],//we must append to get a stylesheet - sheet = storageNode.sheet ? 'sheet' : 'styleSheet', - storage = storageNode[sheet],//css rules must remain in a stylesheet for IE and FF - rules = storage.rules ? 'rules' : 'cssRules', - remove = storage.deleteRule ? 'deleteRule' : 'removeRule', - owner = storage.ownerNode ? 'ownerNode' : 'owningElement', - reRule = /^([^{]+)\{([^}]*)\}/m, - reStyle = /([^:]+):([^;}]+)/; - - storage.disabled = true;//let's ignore your rules - - var $rule = $.rule = function( r, c ){ - if(!(this instanceof $rule)) - return new $rule( r, c ); - - this.sheets = $rule.sheets(c); - if( r && reRule.test(r) ) - r = $rule.clean( r ); - if( typeof r == 'object' && !r.exec ) { - setArray( this, r.get ? r.get() : r.splice ? r : [r] ); - } else { - setArray( this, this.sheets.cssRules().get() ); - if (r) - return this.filter( r ); - } - return this; - }; - - $.extend( $rule, { - sheets:function( c ){ - var o = c; - if( typeof o != 'object' ) - o = $.makeArray(document.styleSheets); - o = $(o).not(storage);//skip our stylesheet - if( typeof c == 'string' ) - o = o.ownerNode().filter(c).sheet(); - return o; - }, - rule:function( str ){ - if( str.selectorText )/* * */ - return [ '', str.selectorText, str.style.cssText ]; - return reRule.exec( str ); - }, - appendTo:function( r, ss, skip ){ - switch( typeof ss ){//find the desired stylesheet - case 'string': ss = this.sheets(ss); - case 'object': - if( ss[0] ) ss = ss[0]; - if( ss[sheet] ) ss = ss[sheet]; - if( ss[rules] ) break;//only if the stylesheet is valid - default: - if( typeof r == 'object' ) return r;//let's not waist time, it is parsed - ss = storage; - } - var p; - if( !skip && (p = this.parent(r)) )//if this is an actual rule, and it's appended. - r = this.remove( r, p ); - - var rule = this.rule( r ); - if( ss.addRule ) - ss.addRule( rule[1], rule[2]||';' );//IE won't allow empty rules - else if( ss.insertRule ) - ss.insertRule( rule[1] + '{'+ rule[2] +'}', ss[rules].length ); - - return ss[rules][ ss[rules].length - 1 ];//return the added/parsed rule - }, - remove:function( r, p ){ - p = p || this.parent(r); - if( p != storage ){//let's save some unnecesary cycles. - var i = p ? $.inArray( r, p[rules] ) : -1; - if( i != -1 ){//if not stored before removal, IE will crash eventually, and some rules in FF get messed up - r = this.appendTo( r, 0 /*storage*/, true );//is faster and shorter to imply storage - p[remove](i); - } - } - return r; - }, - clean:function( r ){ - return $.map( r.split('}'), function( txt ){ - if( txt ) - return $rule.appendTo( txt + '}' /*, storage*/ );//parse the string, storage implied - }); - }, - parent:function( r ){//CSS rules in IE don't have parentStyleSheet attribute - if( typeof r == 'string' || !$.browser.msie )//if it's a string, just return undefined. - return r.parentStyleSheet; - - var par; - this.sheets().each(function(){ - if( $.inArray(r, this[rules]) != -1 ){ - par = this; - return false; - } - }); - return par; - }, - outerText:function( rule ){ - return !rule || !rule.selectorText ? '' : [rule.selectorText+'{', '\t'+rule.style.cssText,'}'].join('\n').toLowerCase(); - }, - text:function( rule, txt ){ - if( txt !== undefined ) - rule.style.cssText = txt; - return !rule ? '' : rule.style.cssText.toLowerCase(); - } - }); - - $rule.fn = $rule.prototype = { - pushStack:function( rs, sh ){ - var ret = $rule( rs, sh || this.sheets ); - ret.prevObject = this; - return ret; - }, - end:function(){ - return this.prevObject || $rule(0,[]); - }, - filter:function( s ){ - var o; - if( !s ) s = /./;//just keep them all. - if( s.split ){ - o = $.trim(s).toLowerCase().split(/\s*,\s*/); - s = function(){ - var s = this.selectorText || ''; - return !!$.grep( s.toLowerCase().split(/\s*,\s*/), function( sel ){ - return $.inArray( sel, o ) != -1; - }).length; - }; - }else if( s.exec ){//string regex, or actual regex - o = s; - s = function(){ return o.test(this.selectorText); }; - } - return this.pushStack($.grep( this, function( e, i ){ - return s.call( e, i ); - })); - }, - add:function( rs, c ){ - return this.pushStack( $.merge(this.get(), $rule(rs, c)) ); - }, - is:function( s ){ - return !!(s && this.filter( s ).length); - }, - not:function( n, c ){ - n = $rule( n, c ); - return this.filter(function(){ - return $.inArray( this, n ) == -1; - }); - }, - append:function( s ){ - var rules = this, rule; - $.each( s.split(/\s*;\s*/),function(i,v){ - if(( rule = reStyle.exec( v ) )) - rules.css( rule[1], rule[2] ); - }); - return this; - }, - text:function( txt ){ - return !arguments.length ? $rule.text( this[0] ) - : this.each(function(){ $rule.text( this, txt ); }); - }, - outerText:function(){ - return $rule.outerText(this[0]); - } - }; - - $.each({ - ownerNode:owner,//when having the stylesheet, get the node that contains it - sheet:sheet, //get the stylesheet from the node - cssRules:rules //get the rules from the stylesheet. - },function( m, a ){ - var many = a == rules;//the rules need some more processing - $.fn[m] = function(){ - return this.map(function(){ - return many ? $.makeArray(this[a]) : this[a]; - }); - }; - }); - - $.fn.cssText = function(){ - return this.filter('link,style').eq(0).sheet().cssRules().map(function(){ - return $rule.outerText(this); - }).get().join('\n'); - }; - - $.each('remove,appendTo,parent'.split(','),function( k, f ){ - $rule.fn[f] = function(){ - var args = $.makeArray(arguments), that = this; - args.unshift(0); - return this.each(function( i ){ - args[0] = this; - that[i] = $rule[f].apply( $rule, args ) || that[i]; - }); - }; - }); - - $.each(('each,index,get,size,eq,slice,map,attr,andSelf,css,show,hide,toggle,'+ - 'queue,dequeue,stop,animate,fadeIn,fadeOut,fadeTo').split(','),function( k, f ){ - $rule.fn[f] = $.fn[f]; - }); - - // this function has been pulled in from jQuery 1.4.1, because it is an internal function and has been dropped as of 1.4.2 - function setArray(rule, elems) { - rule.length = 0; - Array.prototype.push.apply( rule, elems ); - } - - var curCSS = $.curCSS; - $.curCSS = function( e, a ){//this hack is still quite exprimental - return ('selectorText' in e ) ? - e.style[a] || $.prop( e, a=='opacity'? 1 : 0,'curCSS', 0, a )//TODO: improve these defaults - : curCSS.apply(this,arguments); - }; - - /** - * Time to hack jQuery.data for animations. - * Only IE really needs this, but to keep the behavior consistent, I'll hack it for all browsers. - * TODO: This kind of id doesn't seem to be good enough - * TODO: Avoid animating similar rules simultaneously - * TODO: Avoid rules' precedence from interfering on animations ? - */ - $rule.cache = {}; - var mediator = function( original ){ - return function( elm ){ - var id = elm.selectorText; - if( id ) - arguments[0] = $rule.cache[id] = $rule.cache[id] || {}; - return original.apply( $, arguments ); - }; - }; - $.data = mediator( $.data ); - $.removeData = mediator( $.removeData ); - - $(window).unload(function(){ - $(storage).cssRules().remove();//empty our rules bin - }); - -})( jQuery ); \ No newline at end of file +/*! + * jQuery.Rule - CSS Rules manipulation, the jQuery way. + * Copyright (c) 2007 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com + * Dual licensed under MIT and GPL. + * Date: 28/08/2019 + * + * @author Ariel Flesler + * @version 1.1.2 + * + * @id jQuery.rule + * @param {Undefined|String|jQuery.Rule} The rules, can be a selector, or literal CSS rules. Many can be given, comma separated. + * @param {Undefined|String|DOMElement|jQuery) The context stylesheets, all of them by default. + * @return {jQuery.Rule} Returns a jQuery.Rule object. + * + * @example $.rule('p,div').filter(function(){ return this.style.display != 'block'; }).remove(); + * + * @example $.rule('div{ padding:20px;background:#CCC}, p{ border:1px red solid; }').appendTo('style'); + * + * @example $.rule('div{}').append('margin:40px').css('margin-left',0).appendTo('link:eq(1)'); + * + * @example $.rule().not('div, p.magic').fadeOut('slow'); + * + * @example var text = $.rule('#screen h2').add('h4').end().eq(4).text(); + */ +;(function( $ ){ + + /** + * Notes + * Some styles and animations might fail, please report it. + * The plugin needs a style node to stay in the DOM all along to temporarily hold rules. DON'T TOUCH IT. + * Opera requires this style to have alternate in the rel to allow disabling it. + * Rules in IE don't have .parentStylesheet. We need to find it each time(slow). + * Animations need close attention. Programatically knowing which rule has precedence, would require a LOT of work. + * This plugin adds $.rule and also 4 methods to $.fn: ownerNode, sheet, cssRules and cssText + * Note that rules are not directly inside nodes, you need to do: $('style').sheet().cssRules(). + */ + + var storageNode = $(' ').appendTo('head')[0],//we must append to get a stylesheet + sheet = storageNode.sheet ? 'sheet' : 'styleSheet', + storage = storageNode[sheet],//css rules must remain in a stylesheet for IE and FF + rules = storage.rules ? 'rules' : 'cssRules', + remove = storage.deleteRule ? 'deleteRule' : 'removeRule', + owner = storage.ownerNode ? 'ownerNode' : 'owningElement', + reRule = /^([^{]+)\{([^}]*)\}/m, + reStyle = /([^:]+):([^;}]+)/; + + storage.disabled = true;//let's ignore your rules + + var $rule = $.rule = function( r, c ){ + if(!(this instanceof $rule)) + return new $rule( r, c ); + + this.sheets = $rule.sheets(c); + if( r && reRule.test(r) ) + r = $rule.clean( r ); + if( typeof r == 'object' && !r.exec ) { + setArray( this, r.get ? r.get() : r.splice ? r : [r] ); + } else { + setArray( this, this.sheets.cssRules().get() ); + if (r) + return this.filter( r ); + } + return this; + }; + + $.extend( $rule, { + sheets:function( c ){ + var o = c; + if( typeof o != 'object' ) + o = $.makeArray(document.styleSheets); + o = $(o).not(storage);//skip our stylesheet + if( typeof c == 'string' ) + o = o.ownerNode().filter(c).sheet(); + return o; + }, + rule:function( str ){ + if( str.selectorText )/* * */ + return [ '', str.selectorText, str.style.cssText ]; + return reRule.exec( str ); + }, + appendTo:function( r, ss, skip ){ + switch( typeof ss ){//find the desired stylesheet + case 'string': ss = this.sheets(ss); + case 'object': + if( ss[0] ) ss = ss[0]; + if( ss[sheet] ) ss = ss[sheet]; + if( rules in ss ) break;//only if the stylesheet is valid + default: + if( typeof r == 'object' ) return r;//let's not waist time, it is parsed + ss = storage; + } + var p; + if( !skip && (p = this.parent(r)) )//if this is an actual rule, and it's appended. + r = this.remove( r, p ); + + var rule = this.rule( r ); + if( ss.addRule ) + ss.addRule( rule[1], rule[2]||';' );//IE won't allow empty rules + else if( ss.insertRule ) + ss.insertRule( rule[1] + '{'+ rule[2] +'}', ss[rules].length ); + + return ss[rules][ ss[rules].length - 1 ];//return the added/parsed rule + }, + remove:function( r, p ){ + p = p || this.parent(r); + if( p != storage ){//let's save some unnecesary cycles. + var i = p ? $.inArray( r, p[rules] ) : -1; + if( i != -1 ){//if not stored before removal, IE will crash eventually, and some rules in FF get messed up + r = this.appendTo( r, 0 /*storage*/, true );//is faster and shorter to imply storage + p[remove](i); + } + } + return r; + }, + clean:function( r ){ + return $.map( r.split('}'), function( txt ){ + if( txt ) + return $rule.appendTo( txt + '}' /*, storage*/ );//parse the string, storage implied + }); + }, + parent:function( r ){//CSS rules in IE don't have parentStyleSheet attribute + if( typeof r == 'string' || r.parentStyleSheet !== undefined )//if it's a string, just return undefined. + return r.parentStyleSheet; + + var par; + this.sheets().each(function(){ + if( $.inArray(r, this[rules]) != -1 ){ + par = this; + return false; + } + }); + return par; + }, + outerText:function( rule ){ + return !rule || !rule.selectorText ? '' : [rule.selectorText+'{', '\t'+rule.style.cssText,'}'].join('\n').toLowerCase(); + }, + text:function( rule, txt ){ + if( txt !== undefined ) + rule.style.cssText = txt; + return !rule ? '' : rule.style.cssText.toLowerCase(); + } + }); + + $rule.fn = $rule.prototype = { + pushStack:function( rs, sh ){ + var ret = $rule( rs, sh || this.sheets ); + ret.prevObject = this; + return ret; + }, + end:function(){ + return this.prevObject || $rule(0,[]); + }, + filter:function( s ){ + var o; + if( !s ) s = /./;//just keep them all. + if( s.split ){ + o = $.trim(s).toLowerCase().split(/\s*,\s*/); + s = function(){ + var s = this.selectorText || ''; + return !!$.grep( s.toLowerCase().split(/\s*,\s*/), function( sel ){ + return $.inArray( sel, o ) != -1; + }).length; + }; + }else if( s.exec ){//string regex, or actual regex + o = s; + s = function(){ return o.test(this.selectorText); }; + } + return this.pushStack($.grep( this, function( e, i ){ + return s.call( e, i ); + })); + }, + add:function( rs, c ){ + return this.pushStack( $.merge(this.get(), $rule(rs, c)) ); + }, + is:function( s ){ + return !!(s && this.filter( s ).length); + }, + not:function( n, c ){ + n = $rule( n, c ); + return this.filter(function(){ + return $.inArray( this, n ) == -1; + }); + }, + append:function( s ){ + var rules = this, rule; + $.each( s.split(/\s*;\s*/),function(i,v){ + if(( rule = reStyle.exec( v ) )) + rules.css( rule[1], rule[2] ); + }); + return this; + }, + text:function( txt ){ + return !arguments.length ? $rule.text( this[0] ) + : this.each(function(){ $rule.text( this, txt ); }); + }, + outerText:function(){ + return $rule.outerText(this[0]); + } + }; + + $.each({ + ownerNode:owner,//when having the stylesheet, get the node that contains it + sheet:sheet, //get the stylesheet from the node + cssRules:rules //get the rules from the stylesheet. + },function( m, a ){ + var many = a == rules;//the rules need some more processing + $.fn[m] = function(){ + return this.map(function(){ + var prop; + try { + // In Chrome, if stylesheet originates from a different domain, + // ss.cssRules simply won't exist. I believe the same is true for IE, but + // I haven't tested it. + // + // In Firefox, if stylesheet originates from a different domain, trying + // to access ss.cssRules will throw a SecurityError. Hence, we must use + // try/catch to detect this condition in Firefox. + prop = this[a]; + } catch(e) { + // Rethrow exception if it's not a SecurityError. Note that SecurityError + // exception is specific to Firefox. + if(e.name !== 'SecurityError') + throw e; + prop = null; + } + return many ? $.makeArray(prop) : prop; + }); + }; + }); + + $.fn.cssText = function(){ + return this.filter('link,style').eq(0).sheet().cssRules().map(function(){ + return $rule.outerText(this); + }).get().join('\n'); + }; + + $.each('remove,appendTo,parent'.split(','),function( k, f ){ + $rule.fn[f] = function(){ + var args = $.makeArray(arguments), that = this; + args.unshift(0); + return this.each(function( i ){ + args[0] = this; + that[i] = $rule[f].apply( $rule, args ) || that[i]; + }); + }; + }); + + $.each(('each,index,get,size,eq,slice,map,attr,andSelf,css,show,hide,toggle,'+ + 'queue,dequeue,stop,animate,fadeIn,fadeOut,fadeTo').split(','),function( k, f ){ + $rule.fn[f] = $.fn[f]; + }); + + // this function has been pulled in from jQuery 1.4.1, because it is an internal function and has been dropped as of 1.4.2 + function setArray(rule, elems) { + rule.length = 0; + Array.prototype.push.apply( rule, elems ); + } + + var curCSS = $.curCSS; + $.curCSS = function( e, a ){//this hack is still quite exprimental + return ('selectorText' in e ) ? + e.style[a] || $.prop( e, a=='opacity'? 1 : 0,'curCSS', 0, a )//TODO: improve these defaults + : curCSS.apply(this,arguments); + }; + + /** + * Time to hack jQuery.data for animations. + * Only IE really needs this, but to keep the behavior consistent, I'll hack it for all browsers. + * TODO: This kind of id doesn't seem to be good enough + * TODO: Avoid animating similar rules simultaneously + * TODO: Avoid rules' precedence from interfering on animations ? + */ + $rule.cache = {}; + var mediator = function( original ){ + return function( elm ){ + var id = elm.selectorText; + if( id ) + arguments[0] = $rule.cache[id] = $rule.cache[id] || {}; + return original.apply( $, arguments ); + }; + }; + $.data = mediator( $.data ); + $.removeData = mediator( $.removeData ); + + $(window).on("unload", function () { + $(storage).cssRules().remove();//empty our rules bin + }); + +})( jQuery ); diff --git a/package.json b/package.json new file mode 100644 index 0000000..cebf773 --- /dev/null +++ b/package.json @@ -0,0 +1,24 @@ +{ + "name": "jquery.rule", + "version": "1.1.2", + "description": "CSS Rules manipulation, the jQuery way", + "main": "jquery.rule.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/flesler/jquery.rule.git" + }, + "keywords": [ + "jquery", + "css", + "rules" + ], + "author": "Ariel Flesler", + "license": "MIT", + "bugs": { + "url": "https://github.com/flesler/jquery.rule/issues" + }, + "homepage": "https://github.com/flesler/jquery.rule#readme" +}