christine
Version:
Syntactic sugar for HTML/CSS + CoffeeScript
548 lines (384 loc) • 14.6 kB
text/coffeescript
selfClosingTags = ['br', 'img', 'input', 'hr', 'meta', 'link']
headTags = ['meta', 'title', 'style', 'class', 'link']
formatHtml = false
debugMode = false
chrisRootFolder = ''
fs = require 'fs'
Path = require 'path'
coffee = require 'coffee-script'
# LINE TYPES
tagType = 0 #if no another type found and this is not a script
tagFilter = /^\s*[\w\-]+ *(( +\w+)?( *)?( +is( +.*)?)?)?$/i
tagPropertyType = 1 #if found property "something"
tagPropertyFilter = /^\s*[\w\-]+ *".*"/
styleClassType = 2 #if this is tag and the tag is style
styleClassFilter = /^\s*(style|class)\s+[\w:_-]+/i
stylePropertyType = 3 #if found property: something
stylePropertyFilter = /^\s*[^"' ]+ *: *.*/i
stringType = 4 #if found "string"
stringFilter = /^\s*".*"/i
scriptType = 5 #if it is under the script tag
variableType = 6 # if found name = something
variableFilter = /^\s*\w+\s*=\s*[\w\W]+/i
headTagType = 7
headTagFilter = /^\s*(meta|title|link|base)/i
moduleType = 8
moduleFilter = /^\s*include\s*".+.chris"/i
ignorableType = -2
emptyFilter = /^[\W\s_]*$/
commentFilter = /^\s*#/i
countSpaces = (l) ->
x = 0
if l[0] == " "
while l[x] == " "
x+=1
x
analiseType = (l) ->
ln = -1
ln = ignorableType if commentFilter.test l
ln = ignorableType if emptyFilter.test l
ln = stylePropertyType if stylePropertyFilter.test l
ln = tagType if tagFilter.test l
ln = headTagType if headTagFilter.test l
ln = styleClassType if styleClassFilter.test l
ln = tagPropertyType if tagPropertyFilter.test l
ln = stringType if stringFilter.test l
ln = variableType if variableFilter.test l
ln = moduleType if moduleFilter.test l
ln
getHierarchy = (lines) ->
lineLevels = []
lineParents=[]
lastLineOfLevel = [-1]
currentLevel = [0]
currentRealLevel = 0
for x in [0...lines.length]
n = countSpaces lines[x]
#lines[x] = lines[x].slice(n)
if n > currentLevel[currentRealLevel]
lastLineOfLevel.push x - 1
currentLevel.push n
currentRealLevel += 1
while n < currentLevel[currentRealLevel]
if n < currentLevel[currentRealLevel]
currentLevel.pop()
lastLineOfLevel.pop()
currentRealLevel -= 1
lineLevels.push currentRealLevel
lineParents[x] = lastLineOfLevel[lastLineOfLevel.length-1]
lineParents
formatVariable = (l) ->
exportArray = []
varContent = ''
varName = l.split('=')[0]
w = 0
while varName.split(' ')[w] == ''
w += 1
varName = varName.split(' ')[w]
c = l.split('=')
c = c[1].split(' ')
w = 0
while w < c.length
if c[w] != ''
varContent += ' ' if varContent != ''
varContent += c[w]
w += 1
exportArray[0] = varName
exportArray[1] = varContent
exportArray
processVariables = (ls, tps) ->
varNames = []
varContents = []
for x in [0...ls.length]
if tps[x] == variableType
varNames.push formatVariable(ls[x])[0]
varContents.push formatVariable(ls[x])[1]
if tps[x] == stylePropertyType
for f in [0...varNames.length]
ls[x] = ls[x].replace(varNames[f], varContents[f]).replace(varNames[f], varContents[f]).replace(varNames[f], varContents[f]).replace(varNames[f], varContents[f])
ls
# Module processing functions
loadChrisModule = (moduleFilePath) ->
msls = fs.readFileSync(moduleFilePath, 'utf8')
msls = cleanUpFile(msls)
mls = msls.split '\n'
mls
processModules = (ls, f) ->
resultLs = []
moduleLevelFilter = /^\s*/
for x in [0...ls.length]
if moduleFilter.test ls[x]
chrisModulePath = ls[x].split('"')[1]
moduleLines = loadChrisModule(f + '/' + chrisModulePath)
moduleLevel = moduleLevelFilter.exec(ls[x])
moduleLines[l] = moduleLevel + moduleLines[l] for l in [0...moduleLines.length]
moduleLines = processModules(moduleLines, Path.dirname(f + '/' + chrisModulePath))
resultLs = resultLs.concat(moduleLines)
else
resultLs.push ls[x]
resultLs
# MAIN CHRISTINE FUNCTION
exports.christinize = (st) ->
shtml(st)
cleanUpLines = (ls) ->
newLs = []
for x in [0...ls.length]
if analiseType(ls[x]) != -2
newLs.push ls[x]
newLs
shtml = (sourceText) ->
lines = []
resultLines = []
lineTypes = []
lineParents = []
lineNums = []
resultText = ''
lines = sourceText.split '\n'
lines = processModules(lines, chrisRootFolder)
lines = cleanUpLines(lines, lineTypes)
# process types and filter lines
for x in [0...lines.length]
t = analiseType(lines[x])
lineTypes.push t
resultLines.push lines[x]
resultLines = processVariables(resultLines, lineTypes)
lineParents = getHierarchy resultLines
lineNums.push(x) for x in [0...resultLines.length]
resultText += "##{lineNums[x]} #{lineTypes[x]} #{resultLines[x]} - #{lineParents[x]}\n" for x in [0...resultLines.length] if debugMode
resultText += '<!doctype html>'
resultText += '<html>'
resultText += processHead(resultLines, lineParents, lineTypes, lineNums)
resultText += processTag("body", -1, resultLines, lineParents, lineTypes, lineNums)
resultText += '</html>'
resultText
formatTag = (l) ->
# get rid of indentation
sp = countSpaces l
l = l.slice(sp)
tagArray = l.split ' '
cleanTag = []
for x in [0...tagArray.length]
cleanTag.push tagArray[x] if tagArray[x] != ""
finalTag = '<' + cleanTag[0]
if cleanTag.length > 1
if cleanTag[1] != 'is'
finalTag += ' id="' + cleanTag[1] + '"'
x = 0
tagClass = ""
collectClasses = false
while x < cleanTag.length
if collectClasses
tagClass += cleanTag[x]
tagClass += ' ' if x < cleanTag.length - 1
else
if cleanTag[x] == 'is'
collectClasses = true if x < cleanTag.length - 1
x += 1
finalTag += ' class="' + tagClass + '"' if tagClass.length > 0
finalTag
formatProperty = (l) ->
# get rid of indentation
sp = countSpaces l
l = l.slice(sp)
cleanProperty = '="'
propertyNameSearch = /^[\w\-]+( *)?"/i
t = l.match(propertyNameSearch)[0]
t = t.split(" ")[0]
t = t.split('"')[0]
cleanProperty = t + cleanProperty
t = l.split('"')[1]
cleanProperty += t + '"'
cleanProperty
formatStyleProperty = (l) ->
# get rid of indentation
sp = countSpaces l
l = l.slice(sp)
dividerPosition = l.indexOf ':'
propertyAfter = l.slice (dividerPosition + 1)
cleanStyleProperty = l.split(':')[0] + ':'
afterArray = propertyAfter.split ' '
for x in [0...afterArray.length]
if afterArray[x] != ''
cleanStyleProperty += afterArray[x]
cleanStyleProperty += ' ' if x < afterArray.length - 1
cleanStyleProperty
formatString = (l) ->
cleanString = l.split('"')[1]
cleanString
checkSelfClosing = (t) ->
selfClosing = true
for i in [0..selfClosingTags.length]
selfClosing = false if t == selfClosingTags[i]
selfClosing
# the main recursive machines!
processHead = (lines = [], links, types, lineNums) ->
finalHead = '<head>'
# collect children
childStyleNums = []
childTagNums = []
if lines.length > 0
for x in [0...lines.length]
if links[x] == -1
childStyleNums.push x if types[x] == styleClassType
childTagNums.push x if types[x] == headTagType
# process head styles
if childStyleNums.length > 0
finalHead += '<style>'
x = 0
while x < childStyleNums.length
finalHead += '\n' if formatHtml
styleChildLines = []
styleChildTypes = []
p = childStyleNums[x] + 1
while links[p] >= childStyleNums[x]
if p < lines.length
styleChildLines.push lines[p]
styleChildTypes.push types[p]
p += 1
else
break
finalHead += processStyleTag(lines[childStyleNums[x]], styleChildLines, styleChildTypes)
x += 1
finalHead += '</style>'
# process head tags
if childTagNums.length > 0
x = 0
while x < childTagNums.length
finalHead += '\n' if formatHtml
tagChildLines = []
tagChildLinks = []
tagChildTypes = []
tagChildLineNums = []
p = childTagNums[x] + 1
while links[p] >= childTagNums[x]
if p < lines.length
tagChildLines.push lines[p]
tagChildLinks.push links[p]
tagChildTypes.push types[p]
tagChildLineNums.push lineNums[p]
p += 1
else
break
tn = childTagNums[x]
finalHead += processTag(lines[tn], lineNums[tn], tagChildLines, tagChildLinks, tagChildTypes, tagChildLineNums)
x += 1
finalHead += '</head>'
finalHead
processStyleTag = (tagLine, childLines = [], childTypes) ->
finalTag = '#'
finalTag = '.' if tagLine.split(' ')[0] == 'class'
if tagLine.split(' ')[1] == 'tag' #if styling tag, not the id or class
finalTag = ''
finalTag += tagLine.split(' ')[2] + '{'
else
finalTag += tagLine.split(' ')[1] + '{'
for x in [0...childLines.length]
finalTag += formatStyleProperty(childLines[x]) + ';' if childTypes[x] == stylePropertyType
finalTag += '}'
finalTag
processTag = (tagLine, selfLink, childLines = [], childLinks, childTypes, lineNums) ->
# get rid of indentation
sp = countSpaces tagLine
tagLine = tagLine.slice(sp)
tagName = tagLine.split(' ')[0]
finalTag = formatTag tagLine
closable = checkSelfClosing(tagLine.split(' ')[0])
# collect all the children
tagProperties = []
tagStyles = []
childs = []
childStrings = []
variables = []
if childLines.length > 0
for x in [0...childLines.length]
if childLinks[x] == selfLink
tagProperties.push childLines[x] if childTypes[x] == tagPropertyType
tagStyles.push childLines[x] if childTypes[x] == stylePropertyType
childs.push x if childTypes[x] == tagType
childs.push x if childTypes[x] == stringType
childs.push x if childTypes[x] == styleClassType
childs.push x if childTypes[x] == variableType
# add tag properties
if tagProperties.length > 0
for x in [0...tagProperties.length]
tagProperties[x] = formatProperty tagProperties[x]
finalTag += ' ' + tagProperties[x]
# add tag style
if tagStyles.length > 0
finalTag += ' style="'
for x in [0...tagStyles.length]
finalTag += formatStyleProperty(tagStyles[x]) + ';'
finalTag += '"'
finalTag += '>'
#... process child tags, strings, styleTags
x = 0
if tagName!='coffeescript'
while x < childs.length
tl = childs[x]
if childTypes[tl] == stringType
finalTag += formatString(childLines[tl])
if childTypes[tl] == styleClassType
if childLinks[tl] != -1
finalTag += '\n' if formatHtml
styleChildLines = []
styleChildTypes = []
p = tl + 1
while childLinks[p] >= tl
if p < childLines.length
styleChildLines.push childLines[p]
styleChildTypes.push childTypes[p]
p += 1
else
break
finalTag += processStyleTag(childLines[tl], styleChildLines, styleChildTypes)
if childTypes[tl] == tagType
finalTag += '\n' if formatHtml
tagChildLines = []
tagChildLinks = []
tagChildTypes = []
tagChildLineNums = []
p = tl + 1
while childLinks[p] >= tl
if p < childLines.length
tagChildLines.push childLines[p]
tagChildLinks.push childLinks[p]
tagChildTypes.push childTypes[p]
tagChildLineNums.push lineNums[p]
p += 1
else
break
finalTag += processTag(childLines[tl], lineNums[tl], tagChildLines, tagChildLinks, tagChildTypes, tagChildLineNums)
x += 1
else
scriptBefore = ''
for l in [0...childLines.length]
scriptBefore += childLines[l] + '\n'
finalTag = '<script>'
tagName = 'script'
finalTag += coffee.compile(scriptBefore)
# close tag and return final string
if closable
finalTag += '</' + tagName + '>'
finalTag += '\n' if formatHtml
finalTag
cleanUpFile = (sFile) ->
carriageTabTest = /[\r\t]/gmi
rFile = sFile
while carriageTabTest.test(rFile)
rFile = rFile.replace('\r', '\n').replace('\t', ' ')
rFile
exports.christinizeFile = (chrisFilePath) ->
sourceFile = fs.readFileSync(chrisFilePath, 'utf8')
sourceFile = cleanUpFile(sourceFile)
chrisRootFolder = Path.dirname chrisFilePath
christinizedFile = shtml(sourceFile)
fs.writeFile(chrisFilePath + '.html', christinizedFile, -> console.log 'ok')
christinizedFile
exports.christinizeAndSave = (chrisSource) ->
christinizedFile = shtml(chrisSource)
fs.writeFile('./chrisPreview.html', christinizedFile)
exports.christinizeFileWithoutSaving = (chrisFilePath) ->
sourceFile = fs.readFileSync(chrisFilePath, 'utf8')
sourceFile = cleanUpFile(sourceFile)
chrisRootFolder = Path.dirname chrisFilePath
shtml(sourceFile)