This question already has answers here:
How to identify unused CSS definitions from multiple CSS files in a project
(3 answers)
Closed 9 years ago.
I was thinking of writing a script which would tell me:
How often each CSS class defined in my .css file is used in my code
Redundant CSS classes - classes never used
CSS classes hat are referenced that don't exist.
But I just want to make sure something like this doesn't exist already? Does it?
Thanks
Just for fun, I wrote one.
try it
First we need to find our style sheet. In an actual script, this would be written better, but this works on jsFiddle.
var styles = document.head.getElementsByTagName('style');
var css = styles[styles.length - 1].innerHTML;
Then remove comments, and the bodies of each selector (i.e. the stuff between the brackets). This is done because there could be a .com in a background-image property, or any number of other problems. We assume there isn't a } in a literal string, so that would cause problems.
var clean = css.replace(/\/\*.*?\*\//g, '').replace(/\{[^}]*\}/g, ',');
We can find classes with regular expressions, and then count how many of them occur.
var re_class = /\.(\w+)/g;
var cssClasses = {}, match, c;
while (match = re_class.exec(clean)) {
c = match[1];
cssClasses[c] = cssClasses[c] + 1 || 1;
}
I used jsprint for displaying our findings. This shows how many times each class is mentioned in our CSS.
jsprint("css classes used", cssClasses);
Thanks to Google and this answer we can find all elements in the body, and loop through them. By default, we assume no classes were used in our HTML, and all classes used were defined.
var elements = document.body.getElementsByTagName("*");
var neverUsed = Object.keys(cssClasses);
var neverDefined = [];
var htmlClasses = {};
We get each elements class, and split it on the spaces.
for (var i=0; i<elements.length; i++) {
var e = elements[i];
var classes = (e.className || "").split(" ");
This is a three dimensional loop, but it works nicely.
for (var j=0; j<classes.length; j++) {
for (var k=0; k<neverUsed.length; k++) {
We thought classes[j] was never used, but we found a use of it. Remove it from the array.
if (neverUsed[k] === classes[j]) {
neverUsed.splice(k, 1);
}
}
It looks like we found a class that doesn't appear in our CSS. We just need to make sure it's not an empty string, and then push it onto our array.
if (classes[j].length && cssClasses[classes[j]] == null) {
neverDefined.push(classes[j]);
}
Also count the number of times each class is used in HTML.
if (classes[j].length) {
htmlClasses[classes[j]] = htmlClasses[classes[j]] + 1 || 1;
}
}
}
Then display our results.
jsprint("html class usage", htmlClasses);
jsprint("never used in HTML", neverUsed);
jsprint("never defined in CSS", neverDefined);
Related
Are there any vector graphics standards that support variable-thickness paths / strokes, e.g. from a stylus input:
Some amount of smoothing may be acceptable. I'd assume that the best way to store it would be as a regular path (e.g. this) and then point-wise sparse thickness information at various points in the path, with gradients between them.
I have looked at SVG but there doesn't seem to be an element that can support it. Are there any vector graphics standards that can?
A single path as currently implemented does not allow variable thickness. There is a W3.org proposal for SVG standard, but no implementation so far in pure SVG.
There are several implementation of a "path with variable thickness", but that relies on svg objects (eg., multiple paths) and a c++ or javascript functions.
PowerStroke is an implementation of such idea of a variable thickness stroke in Inkscape. A good entry to the source in c++ is here.
There are other implementations in SVG and javascript, relying on multiple paths:
Tubefy, a set of few js functions, the principle is based on a linear interpolation. There are several implementation of Tubefy, the simplest is:
$ = function (id) { return typeof id=='string'?document.getElementById(id):id };
var root = document.rootElement;
function lerp(p, a, b) { return Number(a)+(b-a)*p; }
function lerpA(p, a, b) { var c=[];
for(var i=0; i<a.length; i++) c[i]=lerp(p, a[i], b[i]);
return c;
}
function toCss(a){
for(var i=0; i<a.length; i++) a[i]=Math.round(a[i]);
return "rgb(" + a.join() + ")";
}
Variable Stroke-Width, based on multiple path, which could be the best answer to your needs.
In one of the examples, the js function uses Tubefy and is directly implemented in the svg file:
<script>//<![CDATA[
var op=1, op1=1;
function vsw0(p0, n, g){ p0=$(p0);
var SW=p0.getAttribute('stroke-widths').replace(/ /g,'').split(',');
var T=p0.getTotalLength();
var n_1=n-1, dt=T/n, dash=(dt+1)+','+T;
p0.setAttribute('stroke-dasharray', dash);
for(var i=0; i<n; i++){ p=i/n_1;
var sw=lerp(p, SW[0], SW[1]); // current stroke width
var off=-i*dt; // current dash offset
var c=toCss(lerpA(p, [255,0,0], [255,255,0])); // curr color
var newP=p0.cloneNode(true);
newP.setAttribute('style', 'stroke-width:'+sw+';stroke-dashoffset:'+off+';stroke:'+c);
$(g).appendChild(newP);
}
}
function f(){ $('abg').setAttribute('stroke', $('bg').getAttribute('fill')) }
//]]></script>
</svg>
Unfortunately this has been proposed but not further developed as an SVG standard:
https://www.w3.org/Graphics/SVG/WG/wiki/Proposals/Variable_width_stroke
Your best bet would be to generate your own outline curve based on the desired inner curve and stroke widths.
Adobe Illustrator does this when using their width tool, and Inkscape has a feature which does that too.
So technically to answer your question, the .ai file format does save stroke width information, but when exported to SVG it is a closed path with fill.
I have to write by.cssContainingText() in chrome console .This is regarding protractor.
Inside the implement of by.cssContainingText(cssSelector, searchText), it does two steps:
run querySelectorAll(cssSelector) to get a element list
iterate the element list to find which one's text match the searchText
Following is implement code of cssContainingText(), you also can find it at github
functions.findByCssContainingText = function(cssSelector, searchText, using) {
using = using || document;
if (searchText.indexOf('__REGEXP__') === 0) {
var match = searchText.split('__REGEXP__')[1].match(/\/(.*)\/(.*)?/);
searchText = new RegExp(match[1], match[2] || '');
}
var elements = using.querySelectorAll(cssSelector);
var matches = [];
for (var i = 0; i < elements.length; ++i) {
var element = elements[i];
var elementText = element.textContent || element.innerText || '';
var elementMatches = searchText instanceof RegExp ?
searchText.test(elementText) :
elementText.indexOf(searchText) > -1;
if (elementMatches) {
matches.push(element);
}
}
return matches;
};
It's more complex to write equivalent in browser's console, but you can do as following:
Open stackoverflow site: https://stackoverflow.com/
execute following example code in browser console
expand the return result in console
mouse over on each item of return result in console, then you can notice the mouse will over on the corresponding element on page as following screenshot
Example code:
Array.from(document.querySelectorAll('li > a')).filter((it)=>{return (it.textContent || it.innerText || '').contains('Users')})
Screenshot of example:
This isn't a good way to approach it lets see the syntax
<ul>
<li class="pet">Dog</li>
<li class="pet">Cat</li>
</ul>
// Returns the li for the dog, but not cat.
var dog = element(by.cssContainingText('.pet', 'Dog'));
inside the bracket the first argument is the .classname and the second argument is the actual text .Better write it in script and check it .it'll work
I am changing the vertices position for the link using the following code:
var vertices = link.get('vertices');
for(var j=0;j < vertices.length; j++){
var vertex = vertices[j];
vertex.x += differenceX;
vertex.y += differenceY;
}
However, even though the link vertices seem to be nicely translated, when hovering over the link, the link-tools appear in the previous position as shown in the following screenshot:
I already tried different things including calls to
paper.render()
linkview.update()
unfortunately none of them seem to work...
You should use Backbone.Model.prototype.set to update the attributes instead of modifying them directly. Otherwise no change event is triggered on the model and the view does not update itself.
var oldVertices = link.get('vertices');
var newVertices = [];
for(var j=0; j < oldVertices.length; j++){
var oldVertex = oldVertices[j];
// Create a new object, so you are not modifying
// the previous model's attributes.
// Backbone Model would not trigger a change event if the previous
// and the new value are `deep` equal.
var newVertex = { x: oldVertex.x, y: oldVertex.y }
newVertex.x += differenceX;
newVertex.y += differenceY;
newVertices.push(newVertex);
}
link.set('vertices', newVertices);
If I have a list of items in an array that represent the names of modules:
var phaseNames:Array = new Array("directorsPrep", "checkIO", "pickupPhoto", "pickupPhoto", "syncing", "dailies", "pictureEdit", "soundEdit", "soundMix", "colorCorrection", "finishing");
and I want to go through each one of these and call a function within each instance of each module, how would I go about doing so. So far, I have the following:
private function changeStartViewDate(numDays:Number):void
{
startViewDate = rightDate(startViewDate.getMonth(), startViewDate.getDate() + numDays, startViewDate.getFullYear());
getDateInfo();
determineCalendarWeek();
var phaseNames:Array = new Array("directorsPrep", "checkIO", "pickupPhoto", "pickupPhoto", "syncing", "dailies", "pictureEdit", "soundEdit", "soundMix", "colorCorrection", "finishing");
for (var i:int = 0; i < wholeProject.length; i++)
{
wholeProject[i].moveProject(Number((1-2) * numDays));
}
for (i = 0; i < phaseNames.length; i++)
{
for (var j:int = 0; j < [phaseNames[i]].length; j++)
{
[phaseNames[i]].movePhase(Number((-1) * numDays));
}
}
}
But when I try to save it, I get the following error:
1084: Syntax Error: expecting identifier before dot.
It's telling me the error is on the following line:
[phaseNames[i]].movePhase(Number((-1) * numDays));
I tried doing something like the following, but it didn't work:
[phaseNames[i].movePhase(Number((-1) * numDays))];
The above attempt of trying to figure this out gave me the following error
1064: Invalid metadata.
Any help would be appreciated.
I am going to assume that:
Each value of your phaseNames array references an actual instance of some other class [and not the name of the class]
The instance defined in the phaseNames array is a child of the current class.
You should be able to do something like this:
var childName = phaseNames[0];
var myChild : myObjectType = this[childName];
// then call function
myChild.doStuff();
This approach is not dissimilar to what you have; I'm just doing it in more lines. I'm also adding the this keyword to access the child.
I bet if you tried this, directly, it would work:
this[phaseNames[i]].movePhase(Number((-1) * numDays));
I have to wonder why you haven't created an array of all the instances instead of an array of all the variables names that point to the instances.
i have the following javascript below after i finish an ajax query
all of my images have name="pic"
<script type="text/javascript">
function done() {
var e = document.getElementsByName("pic");
alert(e.length);
for (var i = 0; i < e.length; i++) {
cvi_instant.add(e[i], { shadow: 75, shade: 10 });
}
}
my goal is to apply an image border around using this library:
http://www.netzgesta.de/instant/
the problem is that for some reason this works but it only seem to apply to every other picture instead of every one. any clue why the code above would skip every other element??
EDIT: I added an alert in the loop and it does correctly go 0, 1,2,3,4,5,6 . .
for (var i = 0; i < e.length; i++)
{
alert(i);
cvi_instant.add(e[i], { shadow: 75, shade: 10 });
}
it only seem to apply to every other picture instead of every one
That's a classic sign of destructive iteration.
Consider what happens if, as I'm guessing, the function cvi_instant.add replaces the element named pic with some other element or elements.
getElementsByName returns a ‘live’ NodeList: it is kept up to date every time you make a change to the DOM. So if it had five elements before, after your call to cvi_instant.add it now contains only four: the first node is gone and nodes 1–4 have moved down to positions 0–3.
Now you go around the loop again. i++, so we're looking at element 1. But element 1 is now what was originally element 2! We skipped the original element 1, and we will continue skipping every other element until we reach the end of the (now half as long) list.
Altering a list at the same time as iterating it causes this kind of problem. If the process inside the iteration actually adds elements to the list you can even get an infinite loop!
The quick fix is to iterate the loop backwards. Now you do the last element first, leaving all the other elements in their original positions and causing no skipping:
var e= document.getElementsByName("pic");
for (var i= e.length; i-->0;) {
cvi_instant.add(e[i], { shadow: 75, shade: 10 });
}
Another simple solution if you know you're always going to be removing the element from the list on each call is:
var e= document.getElementsByName("pic");
while (e.length>0) {
cvi_instant.add(e[0], { shadow: 75, shade: 10 });
}
The most general solution is needed when your loop body can do anything to the list, such as inserting new elements named pic at the start of the document or removing other elements from the middle. It is slightly slower but always safe to make a static copy of the list to work from:
function Array_fromList(l) {
var a= [];
for (var i= 0; i<l.length; i++)
a.push(l[i]);
return a;
}
var e= Array_fromList(document.getElementsByName("pic"));
for (var i= 0; i<e.length; i++) {
cvi_instant.add(e[i], { shadow: 75, shade: 10 });
}
My guess is that cvi_instant.add() is doing some incrementing or iteration on the values passed to it. Try doing this instead - it's easier, and I believe it will fix your problem:
function done() {
var e = document.getElementsByName('pic');
for (pic in e) { cvs_instant.add(pic, { shadow: 75, shade: 10 }); }
}
Hi I came across the same problem.
My script was skipping every other
element. I finally solved it by simply
changing the variable name from i to
k in my loop. My guess is therefor
that the variable i is used by
getElementsByTagName internally to
keep track of where it is in the
live nodelist and is leaking out
to the programmers interface somehow.
So its a bug! :-)
-- EDIT:
All of what I claim below appears to be totally wrong. I leave this here as a point for anyone who thought the same :) I tested in FF3. I would love to claim that I saw this behaviour once, in IE, but maybe it was many years ago (come to think of it, it was probably 7 years ago). My memory is probably bad :)
-- OLD:
To slightly expand on my wild guess, if it turns out to be accurate:
From memory, if you don't declare a variable ('var ...') it'll use one from somewhere else.
Thus, without testing, this code:
for(var k = 0; k < 2; k++){
f();
alert("k: " + k);
}
function f () {
k++;
}
Should show the same behaviour. I think TML's solution is quite nice, from a 'defensive coding' point of view, it my analysis turns out to be correct.