PluginProbe ʕ •ᴥ•ʔ
SiteOrigin CSS / 1.1
SiteOrigin CSS v1.1
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 11 years ago csslint.min.js 10 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 10 years ago specificity.js 11 years ago specificity.min.js 10 years ago
inspector.js
483 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 socss.parsedCss[ $$.attr('id') ] = p;
315 });
316 }
317 return socss.parsedCss;
318 };
319
320 /**
321 * Function to get all the available page selectors
322 */
323 socss.fn.pageSelectors = function(){
324 var selectors = [];
325 var parsedCss = socss.fn.getParsedCss();
326
327 for( var k in parsedCss ) {
328 var rules = parsedCss[k].stylesheet.rules;
329 for( var i = 0; i < rules.length; i++ ) {
330 if (typeof rules[i].selectors === 'undefined') {
331 continue;
332 }
333
334 for(var j = 0; j < rules[i].selectors.length; j++) {
335 selectors = selectors.concat( getSelectorSpecificity( rules[i].selectors[j] ) );
336 }
337 }
338 }
339
340 // Also add selectors for all the elements in the
341 $('body *').each(function(){
342 var $$ = $(this);
343 var elName = socss.fn.elSelector( $$ );
344
345 selectors = selectors.concat(getSelectorSpecificity(elName));
346 });
347
348 var $body = $('body');
349 var bName = socss.fn.elSelector($body);
350 selectors = selectors.concat(getSelectorSpecificity(bName, true));
351
352 selectors = _.uniq( selectors, false, function( a ){
353 return a.selector;
354 } );
355
356 selectors.sort(function(a, b){
357 return a.specificity > b.specificity ? -1 : 1;
358 });
359
360 return selectors;
361 };
362
363 socss.fn.elementAttributes = function( el ) {
364 if( !document.styleSheets ) {
365 return [];
366 }
367
368 var elProperties = [];
369
370 var trimFunc = function(e) {
371 return e.trim();
372 };
373
374 var filterFunc = function(e){
375 return e !== '';
376 };
377
378 var splitFunc = function(e) {
379 return e.split(':').map( trimFunc );
380 };
381
382 var parsedCss = socss.fn.getParsedCss();
383
384 var isAtRule = function (ruleType) {
385 switch(ruleType) {
386 case 'charset':
387 case 'custom-media':
388 case 'document':
389 case 'font-face':
390 case 'host':
391 case 'import':
392 case 'keyframes':
393 case 'keyframe':
394 case 'media':
395 case 'namespace':
396 case 'page':
397 case 'supports':
398 return true;
399
400 }
401 return false;
402 };
403
404 for( var k in parsedCss ) {
405 var rules = parsedCss[k].stylesheet.rules;
406 for( var i = 0; i < rules.length; i++ ) {
407 var rule = rules[i];
408 if (
409 typeof rule.selectors === 'undefined' || isAtRule(rule.type)
410 ) {
411 continue;
412 }
413
414 for(var j = 0; j < rule.selectors.length; j++) {
415 var ruleSpecificity = SPECIFICITY.calculate( rule.selectors[j] );
416 for (var l = 0; l < ruleSpecificity.length; l++) {
417 try {
418 if ( el.is( ruleSpecificity[l].selector ) ) {
419 var declarations = rule.declarations;
420 for (var l = 0; l < declarations.length; l++) {
421 elProperties.push({
422 'name': declarations[l].property,
423 'value': declarations[l].value,
424 'specificity': parseInt( ruleSpecificity[l].specificity.replace( /,/g, '' ) )
425 });
426 }
427 }
428 }
429 catch (e) {
430 // For now, we're just going to ignore rules that trigger errors
431 }
432 }
433 }
434
435 }
436 }
437
438 elProperties.sort( function(a,b) {
439 return a.specificity > b.specificity ? 1 : -1;
440 }).reverse();
441
442 var returnProperties = {};
443 for( var pi = 0; pi < elProperties.length; pi++ ) {
444 if( typeof returnProperties[elProperties[pi].name] === 'undefined' ) {
445 returnProperties[elProperties[pi].name] = elProperties[pi].value;
446 }
447 }
448
449 return returnProperties;
450 };
451
452 socss.fn.elSelector = function( el ){
453 var elName = '';
454 if( el.attr('id') !== undefined ) {
455 elName += '#' + el.attr('id');
456 }
457 if( el.attr('class') !== undefined ) {
458 elName += '.' + el.attr('class').replace(/\s+/g, '.');
459 }
460
461 if( elName === '' ) {
462 elName = el.prop('tagName').toLowerCase();
463 }
464
465 return elName;
466 };
467
468 window.socssInspector = socss;
469
470 } ) ( jQuery, _, socssOptions );
471
472 jQuery( function($){
473 var socss = window.socssInspector;
474
475 // Setup the editor
476 var inspector = new socss.view.inspector( {
477 el : $('#socss-inspector-interface').get(0)
478 } );
479 inspector.activate();
480
481 window.socssInspector.mainInspector = inspector;
482 } );
483