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