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