PluginProbe ʕ •ᴥ•ʔ
SiteOrigin CSS / 1.2.13
SiteOrigin CSS v1.2.13
1.2.1 1.2.10 1.2.11 1.2.12 1.2.13 1.2.14 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.2.7 1.2.8 1.2.9 1.3.0 1.3.1 1.3.2 1.4.0 1.4.1 1.4.2 1.4.3 1.5.0 1.5.1 1.5.10 1.5.11 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.5.9 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 trunk 1.0 1.0.1 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.0.7 1.0.8 1.1 1.1.1 1.1.2 1.1.3 1.1.4 1.1.5 1.2.0
so-css / js / inspector.js
so-css / js Last commit date
URI.js 9 years ago URI.min.js 6 years ago css.js 6 years ago css.min.js 6 years ago csslint.js 9 years ago csslint.min.js 6 years ago editor.js 5 years ago editor.min.js 5 years ago inspector.js 5 years ago inspector.min.js 5 years ago jquery.sizes.js 11 years ago jquery.sizes.min.js 6 years ago specificity.js 11 years ago specificity.min.js 6 years ago
inspector.js
504 lines
1
2 /* globals jQuery, Backbone, _, socssOptions, SPECIFICITY, console */
3
4 ( function( $, _, socssOptions ){
5
6 var socss = {
7 model : { },
8 collection : { },
9 view : { },
10 fn : {}
11 };
12
13 var getSelectorSpecificity = function(selector, useParts) {
14 var specificities = [];
15 var ruleSpecificity = SPECIFICITY.calculate( selector );
16 for (var i = 0; i < ruleSpecificity.length; i++) {
17 var specificity = ruleSpecificity[ i ];
18 if ( useParts ) {
19 for ( var j = 0; j < specificity.parts.length; j++ ) {
20 var specificityPart = specificity.parts[ j ];
21 // Recursive call to add specificities for parts.
22 specificities = specificities.concat(getSelectorSpecificity(specificityPart.selector));
23 }
24 } else {
25 specificities.push({
26 'selector': specificity.selector.trim(),
27 'specificity': parseInt(specificity.specificity.replace(/,/g, ''))
28 });
29 }
30 }
31 return specificities;
32 };
33
34 /**
35 * This is the main view for the app
36 */
37 socss.view.inspector = Backbone.View.extend( {
38
39 active: false,
40 hl: false,
41 hoverEl: false,
42 pageSelectors: [],
43
44 selectorTemplate: _.template('<div class="socss-selector"><%= selector %></div>'),
45 importantClasses: [
46 'menu-item-',
47 'postid-',
48 'page-id-',
49 ],
50
51 initialize: function(){
52 var thisView = this;
53
54 this.hl = new socss.view.highlighter();
55 this.hl.initialize();
56
57 this.pageSelectors = socss.fn.pageSelectors();
58
59 // Setup hovering
60 $('body').on('mouseover', '*', function(e){
61 if( !thisView.active ) {
62 return true;
63 }
64
65 var $$ = $(this);
66 if( $$.closest('.socss-element').length === 0 ) {
67 e.stopPropagation();
68 thisView.setHoverEl( $(this) );
69 }
70 });
71
72 // Setup the click event
73 var wcCheck = $( '.single-product' ).length;
74 $('body *').on( 'click', function( e ) {
75 if( !thisView.active || thisView.$el.is(':hover') ) {
76 return true;
77 }
78
79 var $$ = $( this );
80 if ( ! wcCheck || ! $$.parents( '.wc-tabs' ).length ) {
81 e.preventDefault();
82
83 $$.trigger( 'blur' );
84 thisView.setActiveEl( thisView.hoverEl );
85 }
86 });
87
88 this.$('.socss-enable-inspector').on( 'click', function(){
89 thisView.toggleActive();
90 } );
91
92 this.$el.on( 'mouseenter', function() {
93 thisView.hl.clear();
94 } );
95
96 // Try register this inspector with the parent editor
97 try {
98 parent.socss.mainEditor.setInspector( this );
99 }
100 catch( err ){
101 console.log( 'No editor to register this inspector with' );
102 }
103
104 },
105
106 /**
107 * Set the element that's currently being hovered
108 *
109 * @param hoverEl
110 */
111 setHoverEl: function( hoverEl ){
112 this.hoverEl = hoverEl;
113 this.hl.highlight( hoverEl );
114 },
115
116 activate: function(){
117 this.active = true;
118 $('body').addClass('socss-active');
119 $('body').removeClass('socss-inactive');
120 },
121
122 deactivate: function(){
123 this.active = false;
124 $('body').addClass('socss-inactive');
125 $('body').removeClass('socss-active');
126 this.hl.clear();
127 this.$('.socss-hierarchy').empty();
128 },
129
130 /**
131 * Toggle the active status
132 */
133 toggleActive: function(){
134 if( this.active ) {
135 this.deactivate();
136 }
137 else {
138 this.activate();
139 }
140 },
141
142 /**
143 * Set the element that we're busy inspecting
144 * @param el
145 */
146 setActiveEl: function( el ) {
147 var thisView = this;
148
149 var $h = this.$('.socss-hierarchy');
150 $h.empty();
151
152 if ( !el ) {
153 return;
154 }
155
156 if (el.prop('tagName').toLowerCase() !== 'body') {
157 var cel = $(el);
158 do {
159 var selector = socss.fn.elSelector( cel );
160 thisView.importantClasses.forEach( function( importantClass ) {
161 if ( selector.indexOf( importantClass ) >= 0 ) {
162 var selectorRegex = new RegExp( '(' + importantClass + '\\d+)', 'g' );
163 selector = selector.replace( selectorRegex, "<strong>$1</strong>");
164 }
165 } );
166
167 $( this.selectorTemplate( { selector: selector } ) )
168 .prependTo($h)
169 .data('el', cel);
170 cel = cel.parent();
171 } while (cel.prop('tagName').toLowerCase() !== 'body');
172
173 $(this.selectorTemplate({selector: 'body'}))
174 .prependTo($h)
175 .data('el', $('body'));
176
177 this.$('.socss-hierarchy .socss-selector')
178 .on( 'mouseenter', function () {
179 thisView.hl.highlight($(this).data('el'));
180 })
181 .on( 'click', function(e) {
182 e.preventDefault();
183 e.stopPropagation();
184 thisView.setActiveEl($(this).data('el'));
185 });
186 }
187
188 // Scroll all the way left...
189 $h.scrollLeft( 99999 );
190
191 // Now lets add all the CSS selectors
192 var selectors = this.pageSelectors.filter( function(a){
193 // Use try to catch any malformed selectors
194 try {
195 return el.is( a.selector );
196 }
197 catch(err) {
198 return false;
199 }
200 } );
201
202 var container = this.$('.socss-selectors-window').empty();
203
204 _.each( selectors, function( selector ){
205 container.append(
206 $( thisView.selectorTemplate( selector ) )
207 .data( selector )
208 );
209 } );
210 container.find('> div')
211 .on( 'mouseenter', function() {
212 thisView.hl.highlight( $(this).data('selector') );
213 } )
214 .on( 'click', function( e ) {
215 e.preventDefault();
216 e.stopPropagation();
217
218 thisView.trigger( 'click_selector', $(this).data('selector') );
219 } );
220
221 // And the CSS attributes
222 var attributes = socss.fn.elementAttributes(el);
223 container = this.$('.socss-properties-window').empty();
224
225 _.each( attributes, function(v, k){
226 container.append(
227 $( thisView.selectorTemplate( { selector: '<strong>' + k + '</strong>: ' + v } ) )
228 .data( 'property', k + ': ' + v )
229 );
230 } );
231
232 container.find('> div')
233 .on( 'click', function( e ) {
234 e.preventDefault();
235 e.stopPropagation();
236
237 thisView.trigger( 'click_property', $(this).data('property') );
238 });
239
240 // Display the link
241 var link = el.closest('a[href]');
242 var linkContainer = this.$('.socss-link');
243 if( link.length ) {
244 linkContainer.show().find('a')
245 .html( link.attr('href').replace(/[\?&]*so_css_preview=1/, '') )
246 .attr('href', link.attr('href') );
247 }
248 else {
249 linkContainer.hide();
250 }
251
252 this.trigger('set_active_element', el, selectors);
253 }
254
255 } );
256
257 socss.view.highlighter = Backbone.View.extend( {
258 template: _.template( $('#socss-template-hover').html().trim() ),
259 highlighted: [ ],
260
261 highlight: function( els ){
262 this.clear();
263 var thisView = this;
264
265 $(els).each(function(i, el){
266 el = $(el);
267
268 if( !el.is(':visible') ) {
269 // Skip over invisible elements
270 return true;
271 }
272
273 var hl = $( thisView.template() );
274 hl.css({
275 'top' : el.offset().top,
276 'left' : el.offset().left,
277 'width' : el.outerWidth(),
278 'height' : el.outerHeight()
279 }).appendTo( 'body' );
280
281 var g;
282
283 var padding = el.padding();
284 for( var k in padding ) {
285 if( parseInt( padding[k] ) > 0 ) {
286 g = hl.find( '.socss-guide-padding.socss-guide-' + k ).show();
287 if( k === 'top' || k === 'bottom' ) {
288 g.css('height', padding[k]);
289 }
290 else {
291 g.css('width', padding[k]);
292 g.css({
293 'width': padding[k],
294 'top' : padding.top,
295 'bottom' : padding.bottom
296 });
297 }
298 }
299 }
300
301 var margin = el.margin();
302 for( var k in margin ) {
303 if( parseInt( margin[k] ) > 0 ) {
304 g = hl.find( '.socss-guide-margin.socss-guide-' + k ).show();
305 if( k === 'top' || k === 'bottom' ) {
306 g.css('height', margin[k]);
307 }
308 else {
309 g.css('width', margin[k]);
310 }
311 }
312 }
313
314 thisView.highlighted.push( hl );
315 } );
316 },
317
318 clear: function(){
319 while( this.highlighted.length ) {
320 this.highlighted.pop().remove();
321 }
322 }
323 } );
324
325 socss.parsedCss = {};
326 socss.fn.getParsedCss = function(){
327 // Load all the parsed CSS
328 if( Object.keys(socss.parsedCss).length === 0 ) {
329 var parser = window.css;
330 $('.socss-theme-styles').each(function(){
331 var $$ = $(this);
332 var p = parser.parse( $$.html(), {
333 silent: true
334 } );
335 socss.parsedCss[ $$.attr('id') ] = p;
336 });
337 }
338 return socss.parsedCss;
339 };
340
341 /**
342 * Function to get all the available page selectors
343 */
344 socss.fn.pageSelectors = function(){
345 var selectors = [];
346 var parsedCss = socss.fn.getParsedCss();
347
348 for( var k in parsedCss ) {
349 var rules = parsedCss[k].stylesheet.rules;
350 for( var i = 0; i < rules.length; i++ ) {
351 if (typeof rules[i].selectors === 'undefined') {
352 continue;
353 }
354
355 for(var j = 0; j < rules[i].selectors.length; j++) {
356 selectors = selectors.concat( getSelectorSpecificity( rules[i].selectors[j] ) );
357 }
358 }
359 }
360
361 // Also add selectors for all the elements in the
362 $('body *').each(function(){
363 var $$ = $(this);
364 var elName = socss.fn.elSelector( $$ );
365
366 selectors = selectors.concat(getSelectorSpecificity(elName));
367 });
368
369 var $body = $('body');
370 var bName = socss.fn.elSelector($body);
371 selectors = selectors.concat(getSelectorSpecificity(bName, true));
372
373 selectors = _.uniq( selectors, false, function( a ){
374 return a.selector;
375 } );
376
377 selectors.sort(function(a, b){
378 return a.specificity > b.specificity ? -1 : 1;
379 });
380
381 return selectors;
382 };
383
384 socss.fn.elementAttributes = function( el ) {
385 if( !document.styleSheets ) {
386 return [];
387 }
388
389 var elProperties = [];
390
391 var trimFunc = function(e) {
392 return e.trim();
393 };
394
395 var filterFunc = function(e){
396 return e !== '';
397 };
398
399 var splitFunc = function(e) {
400 return e.split(':').map( trimFunc );
401 };
402
403 var parsedCss = socss.fn.getParsedCss();
404
405 var isAtRule = function (ruleType) {
406 switch(ruleType) {
407 case 'charset':
408 case 'custom-media':
409 case 'document':
410 case 'font-face':
411 case 'host':
412 case 'import':
413 case 'keyframes':
414 case 'keyframe':
415 case 'media':
416 case 'namespace':
417 case 'page':
418 case 'supports':
419 return true;
420
421 }
422 return false;
423 };
424
425 for( var k in parsedCss ) {
426 var rules = parsedCss[k].stylesheet.rules;
427 for( var i = 0; i < rules.length; i++ ) {
428 var rule = rules[i];
429 if (
430 typeof rule.selectors === 'undefined' || isAtRule(rule.type)
431 ) {
432 continue;
433 }
434
435 for(var j = 0; j < rule.selectors.length; j++) {
436 var ruleSpecificity = SPECIFICITY.calculate( rule.selectors[j] );
437 for (var l = 0; l < ruleSpecificity.length; l++) {
438 try {
439 if ( el.is( ruleSpecificity[l].selector ) ) {
440 var declarations = rule.declarations;
441 for (var l = 0; l < declarations.length; l++) {
442 elProperties.push({
443 'name': declarations[l].property,
444 'value': declarations[l].value,
445 'specificity': parseInt( ruleSpecificity[l].specificity.replace( /,/g, '' ) )
446 });
447 }
448 }
449 }
450 catch (e) {
451 // For now, we're just going to ignore rules that trigger errors
452 }
453 }
454 }
455
456 }
457 }
458
459 elProperties.sort( function(a,b) {
460 return a.specificity > b.specificity ? 1 : -1;
461 }).reverse();
462
463 var returnProperties = {};
464 for( var pi = 0; pi < elProperties.length; pi++ ) {
465 if( typeof returnProperties[elProperties[pi].name] === 'undefined' ) {
466 returnProperties[elProperties[pi].name] = elProperties[pi].value;
467 }
468 }
469
470 return returnProperties;
471 };
472
473 socss.fn.elSelector = function( el ){
474 var elName = '';
475 if( el.attr('id') !== undefined ) {
476 elName += '#' + el.attr('id');
477 }
478 if( el.attr('class') !== undefined ) {
479 elName += '.' + el.attr('class').replace(/\s+/g, '.');
480 }
481
482 if( elName === '' ) {
483 elName = el.prop('tagName').toLowerCase();
484 }
485
486 return elName;
487 };
488
489 window.socssInspector = socss;
490
491 } ) ( jQuery, _, socssOptions );
492
493 jQuery( function($){
494 var socss = window.socssInspector;
495
496 // Setup the editor
497 var inspector = new socss.view.inspector( {
498 el : $('#socss-inspector-interface').get(0)
499 } );
500 inspector.activate();
501
502 window.socssInspector.mainInspector = inspector;
503 } );
504