PluginProbe ʕ •ᴥ•ʔ
SiteOrigin CSS / 1.0.8
SiteOrigin CSS v1.0.8
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
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
458 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 = new cssjs();
311 $('.socss-theme-styles').each(function(){
312 var $$ = $(this);
313 var p = parser.parseCSS( $$.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 for( var i = 0; i < parsedCss[k].length; i++ ) {
329 if (typeof parsedCss[k][i].selector === 'undefined') {
330 continue;
331 }
332
333 selectors = selectors.concat( getSelectorSpecificity( parsedCss[ k ][ i ].selector ) );
334 }
335 }
336
337 // Also add selectors for all the elements in the
338 $('body *').each(function(){
339 var $$ = $(this);
340 var elName = socss.fn.elSelector( $$ );
341
342 selectors = selectors.concat(getSelectorSpecificity(elName));
343 });
344
345 var $body = $('body');
346 var bName = socss.fn.elSelector($body);
347 selectors = selectors.concat(getSelectorSpecificity(bName, true));
348
349 selectors = _.uniq( selectors, false, function( a ){
350 return a.selector;
351 } );
352
353 selectors.sort(function(a, b){
354 return a.specificity > b.specificity ? -1 : 1;
355 });
356
357 return selectors;
358 };
359
360 socss.fn.elementAttributes = function( el ) {
361 if( !document.styleSheets ) {
362 return [];
363 }
364
365 var elProperties = [];
366
367 var trimFunc = function(e) {
368 return e.trim();
369 };
370
371 var filterFunc = function(e){
372 return e !== '';
373 };
374
375 var splitFunc = function(e) {
376 return e.split(':').map( trimFunc );
377 };
378
379
380 var parsedCss = socss.fn.getParsedCss();
381
382 for( var k in parsedCss ) {
383 for( var i = 0; i < parsedCss[k].length; i++ ) {
384 if (
385 typeof parsedCss[k][i].selector === 'undefined' ||
386 typeof parsedCss[k][i].type !== 'undefined' ||
387 parsedCss[k][i].selector[0] === '@'
388 ) {
389 continue;
390 }
391
392 var ruleSpecificity = SPECIFICITY.calculate( parsedCss[k][i].selector );
393 for (var j = 0; j < ruleSpecificity.length; j++) {
394 try {
395 if( el.is( ruleSpecificity[j].selector ) ) {
396 for( var l = 0; l < parsedCss[k][i].rules.length; l++ ) {
397 elProperties.push({
398 'name' : parsedCss[k][i].rules[l].directive,
399 'value' : parsedCss[k][i].rules[l].value,
400 'specificity' : parseInt(ruleSpecificity[j].specificity.replace(/,/g, ''))
401 });
402 }
403 }
404 }
405 catch( e ) {
406 // For now, we're just going to ignore rules that trigger jQuery errors
407 }
408 }
409
410 }
411 }
412
413 elProperties.sort( function(a,b) {
414 return a.specificity > b.specificity ? 1 : -1;
415 }).reverse();
416
417 var returnProperties = {};
418 for( var pi = 0; pi < elProperties.length; pi++ ) {
419 if( typeof returnProperties[elProperties[pi].name] === 'undefined' ) {
420 returnProperties[elProperties[pi].name] = elProperties[pi].value;
421 }
422 }
423
424 return returnProperties;
425 };
426
427 socss.fn.elSelector = function( el ){
428 var elName = '';
429 if( el.attr('id') !== undefined ) {
430 elName += '#' + el.attr('id');
431 }
432 if( el.attr('class') !== undefined ) {
433 elName += '.' + el.attr('class').replace(/\s+/g, '.');
434 }
435
436 if( elName === '' ) {
437 elName = el.prop('tagName').toLowerCase();
438 }
439
440 return elName;
441 };
442
443 window.socssInspector = socss;
444
445 } ) ( jQuery, _, socssOptions );
446
447 jQuery( function($){
448 var socss = window.socssInspector;
449
450 // Setup the editor
451 var inspector = new socss.view.inspector( {
452 el : $('#socss-inspector-interface').get(0)
453 } );
454 inspector.activate();
455
456 window.socssInspector.mainInspector = inspector;
457 } );
458