Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions js/libs/jquery.htmlClean.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
options.allowEmpty = tagAllowEmpty.concat(options.allowEmpty);

var tagsRE = /(<(\/)?(\w+:)?([\w]+)([^>]*)>)|<!--(.*?--)>/gi;
var tagsOnlyRE = /(<(\/)?(\w+:)?([\w]+)([^>]*)>)/gi;
var attrsRE = /([\w\-]+)\s*=\s*(".*?"|'.*?'|[^\s>\/]*)/gi;

var tagMatch;
Expand Down Expand Up @@ -157,7 +158,7 @@
if (tag.toProtect) {
// skip to closing tag
var tagMatch2;
while (tagMatch2 = tagsRE.exec(html)) {
while (tagMatch2 = tagsOnlyRE.exec(html)) {
var tag2 = new Tag(tagMatch2[4], tagMatch2[1], tagMatch2[5], options);
if (tag2.isClosing && tag2.name == tag.name) {
element.children.push(RegExp.leftContext.substring(lastIndex));
Expand Down Expand Up @@ -581,4 +582,4 @@
// white space chars
var whitespace = [" ", " ", "\t", "\n", "\r", "\f"];

})(jQuery);
})(jQuery);
7 changes: 7 additions & 0 deletions js/panel.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
webkitPropertiesFilter = new WebkitPropertiesFilter(),
defaultValueFilter = new DefaultValueFilter(),
sameRulesCombiner = new SameRulesCombiner(),
htmlStylesCombiner = new HTMLStylesCombiner(),
inspectedContext = new InspectedContext(),

loader = $('#loader'),
Expand All @@ -23,6 +24,7 @@
combineSameRulesInput = $('#combine-same-rules'),
fixHTMLIndentationInput = $('#fix-html-indentation'),
includeAncestors = $('#include-ancestors'),
combineCssIntoHTML = $('#combine-css-to-html'),
idPrefix = $('#id-prefix'),

htmlTextarea = $('#html'),
Expand Down Expand Up @@ -67,6 +69,7 @@
fixHTMLIndentationInput.on('change', persistSettingAndProcessSnapshot);
combineSameRulesInput.on('change', persistSettingAndProcessSnapshot);
includeAncestors.on('change', persistSettingAndProcessSnapshot);
combineCssIntoHTML.on('change', persistSettingAndProcessSnapshot);

createButton.on('click', makeSnapshot);

Expand Down Expand Up @@ -218,6 +221,10 @@
});
}

if (combineCssIntoHTML.is(':checked')) {
html = htmlStylesCombiner.process(html, styles);
}

styles = cssStringifier.process(styles);

if (isValidPrefix(idPrefix.val())) {
Expand Down
144 changes: 144 additions & 0 deletions js/processors/HTMLStylesCombiner.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
/**
* Injects the CSS into the HTML as Style attributes.
*
* @constructor
*/
function HTMLStylesCombiner() {
"use strict";
var cursor = 0,
stylesMap,

// constants
ATTRIBUTE_ENCLOSING_CHARACTERS = ['"', "'"],
ESCAPING_CHARACTER = '\\',
ID_ATTRIBUTE = "id=",
STYLE_ATTRIBUTE = "style=";

/**
* Looks for the next 'id' attribute inside a tag. Returns -1 if not found.
*/
function getNextIdAttributePosition(html, lastCursor) {
var currentCursor,
tagStartCursor,
tagEndCursor,
idCursor;

while (lastCursor >= 0) {
tagStartCursor = html.indexOf("<", lastCursor);
if (tagStartCursor < 0) {
return -1;
}
tagEndCursor = html.indexOf(">", tagStartCursor);
if (tagEndCursor < 0) {
return -1;
}
currentCursor = tagStartCursor;
do {
idCursor = html.indexOf(ID_ATTRIBUTE, currentCursor);
if (idCursor < 0) {
return -1;
} else if (ATTRIBUTE_ENCLOSING_CHARACTERS.indexOf(html.charAt(idCursor + ID_ATTRIBUTE.length)) < 0) {
// Not the right 'id=', look for the next
currentCursor++;
} else if (idCursor < tagEndCursor) {
// Finally!
return idCursor;
}
} while (idCursor < tagEndCursor);
lastCursor = tagEndCursor;
}
}

/**
* Extracts the attribute value that is in the current position.
* @param html the text to extract from.
* @param attributeEnclosingChar the string/character that encloses the value.
* @returns {*} The value that relates to the closest attribute, or null if not found.
*/
function extractValueInCurrentPosition(html, attributeEnclosingChar) {
var idStartIndex,
idEndIndex;

idStartIndex = html.indexOf(attributeEnclosingChar, cursor) + 1;
idEndIndex = html.indexOf(attributeEnclosingChar, idStartIndex + 1);
if (idStartIndex < 0 || idEndIndex < 0) {
return null;
}

return html.substring(idStartIndex, idEndIndex);
}

/**
* Converts SnappySnippet's CSS object into a string of CSS properties.
* @param properties The CSS object to extort.
* @param attributeEnclosingChar The string/character that encloses values.
* @returns {string} CSS properties contained in the given object.
*/
function propertiesToString(properties, attributeEnclosingChar) {
var propertyName,
output = "";

for (propertyName in properties) {
if (properties.hasOwnProperty(propertyName)) {
// Treat those special url() functionals, that sometimes have quotation marks although they are not required
var propertyValue =
properties[propertyName].replace(/url\("(.*)"\)/g, "url($1)").replace(/url\('(.*)'\)/g, "url($1)")
.replace(attributeEnclosingChar, ESCAPING_CHARACTER + attributeEnclosingChar);
output += propertyName + ": " + propertyValue + "; ";
}
}

return output;
}

/**
* Injects style attribute to the current position in the HTML.
* @param html The text to use.
* @param styleId What key we are currently on.
* @param attributeEnclosingChar The string/character that encloses values.
* @returns {*} the modified string.
*/
function insertStyleAtIndex(html, styleId, attributeEnclosingChar) {
var cssStyles = stylesMap[styleId] && stylesMap[styleId].node;

if (!cssStyles) {
return html;
}
return html.substring(0, cursor) + // The head of the string
STYLE_ATTRIBUTE + attributeEnclosingChar + // The attribute key
propertiesToString(stylesMap[styleId].node, attributeEnclosingChar) + // The attribute value
attributeEnclosingChar + " " + // Closing the value just before the next attribute
html.substring(cursor); // The tail of the string
}

this.process = function (html, styles) {
var currentId,
attributeEnclosingChar;

// Sanity check
if (Boolean(html) && Boolean(styles)) {
// Prepare a lookup dictionary of styles by the respective element id
stylesMap = styles.map(function (styleObj) {
var keyValuePair = {};
keyValuePair[styleObj.id] = styleObj;
return keyValuePair;
}).reduce(function (mergedObj, currentObj) {
return $.extend(mergedObj, currentObj);
});

cursor = getNextIdAttributePosition(html, 0);
while (cursor >= 0) {
// Make use of the fact that attribute value is always enclosed with the same char (either " or ')
attributeEnclosingChar = html.charAt(cursor + ID_ATTRIBUTE.length);
currentId = extractValueInCurrentPosition(html, attributeEnclosingChar);
if (currentId === null)
break;
html = insertStyleAtIndex(html, currentId, attributeEnclosingChar);

cursor = getNextIdAttributePosition(html, cursor);
}
}

return html;
};
}
File renamed without changes.
9 changes: 7 additions & 2 deletions panel.html
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,13 @@ <h4 class="panel-title">
<li><label class="checkbox">
<input type='checkbox' id='include-ancestors'/> Include ancestor elements in the snippet
</label></li>
<li><label class="checkbox">
<input type='checkbox' id='combine-css-to-html'/> Incorporate CSS into HTML as style
attributes
</label></li>
<li>
<div class="form-group">
Prefix all CSS IDs with <input type='text' class="form-control" id='id-prefix' placeholder="prefix_"/>
Prefix all CSS IDs with <input type='text' class='form-control' id='id-prefix' placeholder='prefix_'/>
</div>
</li>
</ul>
Expand Down Expand Up @@ -149,10 +153,11 @@ <h4 class="panel-title">

<script src='js/libs/jquery.htmlClean.js'></script>
<script src='js/tools/CSSStringifier.js'></script>
<script src='js/tools/SameRulesCombiner.js'></script>
<script src='js/processors/SameRulesCombiner.js'></script>
<script src='js/filters/DefaultValueFilter.js'></script>
<script src='js/filters/ShorthandPropertyFilter.js'></script>
<script src='js/filters/WebkitPropertiesFilter.js'></script>
<script src='js/processors/HTMLStylesCombiner.js'></script>
<script src='js/tools/Snapshooter.js'></script>
<script src='js/tools/InspectedContext.js'></script>
<script src='js/panel.js'></script>
Expand Down