UNPKG

syntax-highlighting

Version:

Syntax Highlighting Colorschemes for many applications

278 lines (241 loc) 8.52 kB
/* * jQuery Scoped CSS plugin * This adds support for the CSS scoped attribute to limit a block of style declarations * to a specific area of the HTML. You can also use @import and media filters in scoped blocks * http://www.w3.org/TR/html5/semantics.html#the-style-element * * Simon Madine, 1 February 2011 * * Use: * Include this plugin file (minified, ideally) and call $.scoped() on load * * Limitations: * - If you're using multiple nested declarations, Webkit might apply different inheritance * specificity rules from the other engines. I don't know who's right. * - Sometimes there are delays parsing externally loaded stylesheets (via @import) and they * might get skipped. Not often but it happens. * * Notes: * - If the browser natively supports <style scoped>, this will return without doing anything * - Style elements really shouldn't have classes added to them. This functionality should * probably use some kind of data attribute. * - Currently, getElementStyles is hand-rolled and probably wrong. * * v0.6 2013-04-08 * Removed $.browser requirement and refactored a bit so it runs slightly faster and works with * jQuery 1.9. Also checks for native scoped style support and exits early if available. * * v0.5 2011-01-30 * Sibling blocks work, most nested blocks work but some oddness in Webkit means that some * styles don't inherit correctly when there are multiple nested declarations. * * v0.4 2011-01-29 * First jQuery plugin version. Works for most cases but gets confused when there are * multiple scoped blocks affecting the same context (siblings). * */ (function($) { "use strict"; //Add this to the global jQuery object as we want to apply this once to the entire document $.scoped = function() { if ('scoped' in document.createElement('style')) { return; } // To detect which method to use when setting styles // IE9 pretends to use setProperty but doesn't var styleSettable = true; try { document.createElement("div").style.setProperty("opacity", 0, ""); } catch (error) { styleSettable = false; } scopedReset(); //Backup the original styles backupBlocks(); //Go through once to add dependencies var $style = $('style'); $style.each(function(index) { var $this = $(this); if (isScoped($this)) { $this.addClass('this_is_' + index); //Get all style blocks in this scope //Including nested blocks $this.parent().find('style').each(function() { //Add a dependency class here to check for later $(this).addClass('depends_on_' + index); }); } }); //Go through a second time to process the scopes $style.each(function() { var $this = $(this); if (isScoped($this)) { //Empty all scoped style blocks //Except those that this context is dependant on emptyBlocks($this); var holdingArea = []; //Read all styles and copy them to a holding area var $parent = $this.parent(), $all = $parent.find('*'); $all.add($parent).each(function() { $(this).css('cssText', ''); if (this.nodeName !== 'STYLE') { holdingArea.push(getStylesText(this)); } }); //Copy all the styles back from the holding area onto the in-scope elements $all.add($parent).each(function() { var this_style, n; if (this.nodeName !== 'STYLE') { if (!$(this).data('originalInline')) { $(this).data('originalInline', $(this).attr('style')); } this_style = holdingArea.shift(); if (typeof this_style === 'string') { // Webkit, Gecko $(this).css('cssText', this_style); } else { if (styleSettable) { // Opera for (n in this_style) { if (n !== 'content' || this_style[n] !== 'none') { try { this.style.setProperty(n, this_style[n]); } catch (err) {} } } } else { // IE for (n in this_style) { try { if (this && this.style && n && this_style[n] && n !== '' && this_style[n] !== '') { this.style[n] = this_style[n]; } } catch (err) {} } } } } }); //Put all other style blocks back fillBlocks(); } }); //Measurements done and styles applied, now clear styles from this style block //This will stop them affecting out-of-scope elements $('style').each(function(i, e) { if (isScoped(e)) { try { e.innerHTML = ''; } catch (error) { $(e).attr('disabled', 'disabled'); } } }); //Standard jQuery attribute selector $('style[scoped]') doesn't //work with empty boolean attributes so this is used instead function isScoped(styleBlock) { return ($(styleBlock).attr('scoped') !== undefined); } //Save all style tag contents to a data object function backupBlocks() { $('style').each(function(i, e) { if (isScoped(e)) { var $e = $(e); $e.data('original-style', $e.html()); } }); } //Each style block now has class="depends_on_1 depends_on_2" //We switch off all the scoped style blocks not mentioned in that list //The disabled attribute only works on IE but coincidentally, //IE doesn't allow .html() on style blocks. function emptyBlocks(currentBlock) { $('style').each(function(i, e) { if (isScoped(e)) { if (!currentBlock.hasClass('depends_on_' + i)) { try { e.innerHTML = ''; } catch (error) { $(e).attr('disabled', 'disabled'); } } } }); } //Put all the styles back to reset for the next loop function fillBlocks() { $('style').each(function(i, e) { var $e = $(e); if (isScoped(e)) { try { e.innerHTML = $e.data('original-style'); } catch (error) { $(this).removeAttr('disabled'); } } }); } //Update this bit with some jQuery magic later function getStylesText(element) { var temp, styles, n, key; if (element.currentStyle) { //We work with a style object in IE and Opera rather than the text sleep(50); //50ms delay to allow for external stylesheet parsing. return element.currentStyle; } //We extract and process the ComputedCSSStyleObject into text temp = document.defaultView.getComputedStyle(element, null); styles = ''; for (n in temp) { if (parseInt(n, 10)) { key = camelize(temp[n]); if (temp[key] !== undefined) { styles += temp[n] + ':' + temp[key] + ';\n'; } } } return styles; } function scopedReset() { var $style = $('style'); $style.each(function(i, styleBlock) { var $styleBlock = $(styleBlock); var $parent = $(this).parent(), $all = $parent.find('*'); $all.add($parent).each(function() { var $this = $(this); if (this.nodeName !== 'STYLE') { if ($this.data('scopedprocessed')) { $this.attr('style', $this.data('originalInline') || ''); $this.data('scopedprocessed', true); } } }); if (($styleBlock.attr('scoped') !== undefined)) { try { if ($styleBlock.data('scopedprocessed')) { this.innerHTML = $styleBlock.data('original-style'); } } catch (error) { $styleBlock.removeAttr('disabled'); } $styleBlock.data('scopedprocessed', true); } }); } //from Prototype function camelize(string) { return string.replace(/-+(.)?/g, function(match, chr) { return chr ? chr.toUpperCase() : ''; }); } function sleep(ms) { var dt = new Date(); dt.setTime(dt.getTime() + ms); while (new Date().getTime() < dt.getTime()) { $.noop(); } } }; })(jQuery);