Parent script for parsing css-code (as string or as external file) and applying this style to a tagged text (html or custom tags) which will result in a text member formatted accordingly
--****************************************************************************
-- Software: CSS CLASS
-- Version: 0.2 (pre-alpha)
-- Date: 2004/03/16
-- Author: Valentin Schmidt (script uses some code from John Dowdell's html-parser
-- and Thomas Björk's php css-parser class)
-- homepage: http://staff.dasdeck.de/valentin/lingo/css_class/
-- Contact: valentin@dasdeck.de
-- License: Freeware
--
-- Requirements/Dependencies:
-- - fileIO Xtra for opening external css-files
-- - PRegEx Xtra (or DMX2004) for removing comments (and speeding up other search&replace actions)
--
-- You may use and modify this software as you wish.
--****************************************************************************
property pCss
property pSrcStr
property pTextMem
property pCurrentChunk
property pDefaultStyle -- applied to all text if not overwritten by css
property pStyleStack -- used for nested tags
property pTagStack -- used for nested tags
property pIsXHTML -- tagged text is (x)html
property pRegExFlag -- RegExp available
property pPRegExFlag -- PRegEx Xtra available
property pJsRegExFlag -- RegExp available via JavaScript (DMX2004 or newer)
property pLink
--**************************************
-- PUBLIC
--**************************************
----------------------------------------
--
----------------------------------------
on new me
pCss=[:]
pDefaultStyle=["font-family":"Arial","font-size":12,"color":"#000000","text-align":"left","white-space":"pre"] --"line-height":14
pIsXHTML=FALSE
-- check for utilities-script
if member("UTILITIES").type<>#script then
alert("CSSParser needs movie-script 'UTILITIES'!")
halt()
end if
-- check for RegExp (PRegEx Xtra or DMX2004)
pPRegExFlag=xtraPresent("PRegEx")
if (NOT pPRegExFlag) AND float((the environment).productVersion)>=10 then
if member("~regexp").memberNum<1 then
s=new(#script)
s.name="~regexp"
s.scripttype=#movie
s.scriptsyntax=#javascript
s.scripttext="function _js_RegExReplace(s,pat,rep){return s.replace(RegExp(pat,'g'),rep);}"
end if
pJsRegExFlag=1
end if
pRegExFlag=(pPRegExFlag OR pJsRegExFlag)
if NOT pRegExFlag then put "Warning: No search&replace with regular expressions available!" -- Script won't remove comments!"
return me
end
----------------------------------------
-- replaces DefaultStyle with given cssStyle
-- cssStyle: proplist, e.g. ["font-family":"Arial","font-size":12,"color":"#000000"]
----------------------------------------
on setDefaultStyle (me, cssStyle)
pDefaultStyle=cssStyle
end
----------------------------------------
-- adds cssStyle to DefaultStyle (and replaces all identical props)
-- cssStyle: proplist, e.g. ["font-family":"Arial","font-size":12,"color":"#000000"]
----------------------------------------
on addDefaultStyle (me, cssStyle)
cnt = cssStyle.count
repeat with i = 1 to cnt
pDefaultStyle[cssStyle.getPropAt(i)]=cssStyle[i]
end repeat
end
----------------------------------------
-- parses css file
----------------------------------------
on parseCSSFile (me, cssFile)
me.parseCSS(readTxtFile(cssFile))
end
----------------------------------------
-- parses css string
----------------------------------------
on parseCSS (me, cssStr)
-- Remove comments (needs PRegEx Xtra or DMX2004)
if pRegExFlag then
cssStr=me._regReplace(cssStr, "/*.+?*/(
|
)*","")
end if
parts = explode("}",cssStr)
parts.deleteAt(parts.count)
repeat with part in parts
l=explode("{",part)
keyStr = trim(l[1])
codeStr = trim(l[2])
keys=explode(",", keyStr)
repeat with aKey in keys
if aKey.length>0 then
if pRegExFlag then
aKey = me._regReplace(aKey, "(
|
)", "")
else
aKey = str_replace(numtochar(10), "", aKey)
aKey = str_replace(numtochar(13), "", aKey)
end if
me._addKey(aKey,codeStr)
end if
end repeat
end repeat
end
----------------------------------------
-- returns css text for given key (or empty string, if key doesn't exist)
----------------------------------------
on getKey (me, aKey)
aKey = strtolower(aKey)
l=explode(":",aKey)
tag=l[1]
if l.count>1 then subtag=l[2]
else subtag=""
l=explode(".",tag)
tag=l[1]
if l.count>1 then class=l[2]
else class=""
l=explode("#",tag)
tag=l[1]
if l.count>1 then id=l[2]
else id=""
res = [:]
cnt=pCss.count
repeat with i=1 to cnt
_tag=pCss.getPropAt(i)
val=pCss[i]
l=explode(":",_tag)
_tag=l[1]
if l.count>1 then _subtag=l[2]
else _subtag=""
l=explode(".",_tag)
_tag=l[1]
if l.count>1 then _class=l[2]
else _class=""
l=explode("#",_tag)
_tag=l[1]
if l.count>1 then _id=l[2]
else _id=""
tagmatch = (tag=_tag) OR (_tag.length=0)
subtagmatch = (subtag=_subtag) OR (_subtag.length=0)
classmatch = (class=_class) OR (_class.length=0)
idmatch = (id=_id)
if (tagmatch AND subtagmatch AND classmatch AND idmatch) then
if aKey starts "#" then
temp="#"&id
else
temp = _tag
if ((temp.length > 0) AND (_class.length > 0)) then
temp = temp & "." & _class
else if (temp.length=0) then
temp = "." & _class
end if
if ((temp.length > 0) AND (_subtag.length > 0)) then
temp = temp & ":" & _subtag
else if (temp.length=0) then
temp = ":" & _subtag
end if
end if
cnt2=pCss[temp].count
repeat with j=1 to cnt2
res[pCss[temp].getPropAt(j)] = pCss[temp][j]
end repeat
end if
end repeat
return res
end
----------------------------------------
-- returns css text for given key and prop (or empty string, if key or prop doesn't exist)
----------------------------------------
on getKeyProp (me, aKey, aProp)
l=me.getKey(aKey)
return string(l[aProp])
end
----------------------------------------
-- apply css to text member based on tagged text
-- optional flag isXHTML sets some defaults for html-tags, turns off pre-formatted wordwrapping and
-- turns on adding of new lines after "div","/div","p","/p","h1","/h1","h2","/h2","h3","/h3"
----------------------------------------
on applyCSS (me, txtMem, taggedText, isXHTML)
if pRegExFlag then
-- remove html-comments, xml- and doctype-declarations (needs PRegEx Xtra or DMX2004)
-- warning: simple and dirty solution: all and ..> removed
taggedText=me._regReplace(taggedText, "<[!?].+?>(
|
)*", "")
end if
pIsXHTML=(isXHTML=TRUE)
if pIsXHTML then
-- TEST: remove HTML/HEAD/BODY
if pRegExFlag then taggedText=me._regReplace(taggedText, "((.*?)|({0,1}(html|body).*?>))(
|
)*", "")
pDefaultStyle["white-space"]="normal" -- html wordwrapping
-- add css for some html tags; you're welcome to add more (like strong, big, small, h1, ...)
me._addKey("b", "font-weight: bold;")
me._addKey("i", "font-style: italic;")
me._addKey("u", "text-decoration: underline;")
me._addKey("pre", "white-space: pre; font-family: Courier;")
me._addKey("a", "text-decoration: underline; color: #0000ff;")
me._addKey("a:visited", "color: #ff00ff;")
end if
----------------------------------------
-- adds key-codeString pair to pCss
----------------------------------------
on _addKey (me, aKey, codeStr)
aKey = strtolower(aKey)
codeStr = strtolower(codeStr)
if (voidP(pCss[aKey])) then
pCss[aKey] = [:]
end if
codes = explode(";",codeStr)
repeat with code in codes
code = trim(code)
l=explode(":",code)
codekey=l[1]
if l.count>1 then codevalue=l[2]
else codevalue=""
if (codekey.length > 0) then
pCss[aKey][trim(codekey)] = trim(codevalue)
end if
end repeat
end
----------------------------------------
-- deletes rest of tag
----------------------------------------
on _deleteRestOfTag me
the itemDelimiter = ">"
delete item 1 of pSrcStr
end
----------------------------------------
-- handles text between tags
----------------------------------------
on _appendText me
the itemDelimiter = "<"
pCurrentChunk = pSrcStr.item[1]
-- check for wordwrap behaviour:
-- "white-space"="pre" (or not declared): keep word-wraps
-- "white-space"="normal": remove word-wraps
whiteSpace=pDefaultStyle["white-space"]
repeat with aStyle in pStyleStack
cnt=aStyle.count
repeat with i = 1 to cnt
white=aStyle["white-space"]
if not voidP(white) then whiteSpace=white
end repeat
end repeat
if whiteSpace="normal" then
if pRegExFlag then
pCurrentChunk = me._regReplace (pCurrentChunk, "(
|
)", "")
else
pCurrentChunk = str_replace(numtochar(10), "", pCurrentChunk)
pCurrentChunk = str_replace(numtochar(13), "", pCurrentChunk)
end if
end if
delete item 1 of pSrcStr
put the itemDelimiter before pSrcStr
if pCurrentChunk<>"" AND pTagStack=[] then -- not inside any tag, format with default style
put pCurrentChunk after pTextMem
end if
end
----------------------------------------
-- handles start and end tags
----------------------------------------
on _handleTag me
the itemDelimiter = ">"
fullTag = ltrim(pSrcStr.item[1])
delete char 1 of fullTag
theTag = fullTag.word[1]
if (theTag starts "/") then -- END TAG
if pCurrentChunk<>"" then
von=pTextMem.text.length+1
bis=von+pCurrentChunk.length
put pCurrentChunk after pTextMem
if theTag="/a" AND pIsXHTML then -- add hyperlink
pTextMem.char[von..bis].hyperlink=pLink
end if
-- go through stack
cssStyle=pDefaultStyle.duplicate()
repeat with aStyle in pStyleStack
cnt=aStyle.count
repeat with i = 1 to cnt
cssStyle[aStyle.getPropAt(i)]=aStyle[i]
end repeat
end repeat
me._applyStyle(pTextMem.char[von..bis].ref,cssStyle)
end if
else if the last char of theTag="/" then -- SINGLE TAG
if pIsXHTML AND (theTag="br/") then put RETURN after pTextMem
else -- START TAG
-- find class and id
cnt=fullTag.word.count
if cnt>1 then
repeat with i=2 to cnt
w=fullTag.word[i]
if (w starts "class=") then
if pRegExFlag then theClass=me._regReplace(trim(w.char[7..w.char.count]), QUOTE, "")
else theClass=str_replace(QUOTE,"",trim(w.char[7..w.char.count]))
theClass=strtolower(theClass)
else if (w starts "id=") then
if pRegExFlag then theId=me._regReplace(trim(w.char[4..w.char.count]), QUOTE, "")
else theId=str_replace(QUOTE,"",trim(w.char[4..w.char.count]))
theId=strtolower(theId)
else if (w starts "href=" AND pIsXHTML) then
put "LINK"
if pRegExFlag then pLink=me._regReplace(trim(w.char[6..w.char.count]), QUOTE, "")
else pLink=str_replace(QUOTE,"",trim(w.char[6..w.char.count]))
end if
end repeat
end if
-- NESTED TAGS
if pTagStack<>[] then
if pCurrentChunk<>"" then
von=pTextMem.text.length+1
bis=von+pCurrentChunk.length
put pCurrentChunk after pTextMem
-- go through stack
cssStyle=pDefaultStyle.duplicate()
repeat with aStyle in pStyleStack
cnt=aStyle.count
repeat with i = 1 to cnt
prop=aStyle.getPropAt(i)
cssStyle[prop]=aStyle[i]
end repeat
end repeat
me._applyStyle(pTextMem.char[von..bis].ref,cssStyle)
end if
end if
styleTag=theTag
if not voidP(theClass) then
styleTag=styleTag&"."&theClass
end if
cssStyle=me.getKey(styleTag)
if not voidP(theId) then
aStyle=me.getKey("#"&theId)
cnt=aStyle.count
repeat with i = 1 to cnt
cssStyle[aStyle.getPropAt(i)]=aStyle[i]
end repeat
end if
-- put tag and corresponding style on stack
pTagStack.add(styleTag)
pStyleStack.add(cssStyle)
end if
if pIsXHTML then
if ["div","/div","p","/p","h1","/h1","h2","/h2","h3","/h3"].getPos(theTag) then
put RETURN after pTextMem
end if
end if
me._deleteRestOfTag()
pCurrentChunk=""
end
----------------------------------------
-- converts css-style-proplist to director-text-style-props and applies it to text-member-reference
----------------------------------------
on _applyStyle (me, txtRef, cssStyle)
dirStyle=[:]
fntStyle=[]
cnt=cssStyle.count
repeat with i = 1 to cnt
k=cssStyle.getPropAt(i)
v=cssStyle[i]
case (k) of
"color":
dirStyle[#color]=RGB(v)
"font-family":
dirStyle[#font]=v
"font-size":
dirStyle[#fontsize]=integer(v)
"line-height":
dirStyle[#fixedLineSpace]=integer(v)
"font-weight": -- bold, normal
if v="bold" then fntStyle.add(#bold)
"font-style": -- italic , normal
if v="italic" then fntStyle.add(#italic)
"text-decoration": -- underline, none
if v="underline" then fntStyle.add(#underline)
"vertical-align": -- super, sub, baseline
if v="super" then fntStyle.add(#superscript)
else if v="sub" then fntStyle.add(#subscript)
"text-align":
if v="justify" then v="Full"
dirStyle[#alignment]=symbol(v)
"margin-left":
dirStyle[#leftIndent]=integer(v)
"margin-right":
dirStyle[#rightIndent]=integer(v)
"margin-bottom":
dirStyle[#bottomSpacing]=integer(v)
"text-indent":
dirStyle[#firstIndent]=integer(v)
end case
end repeat
if fntStyle=[] then fntStyle=[#plain]
dirStyle[#fontStyle]=fntStyle
cnt=dirStyle.count
repeat with i = 1 to cnt
txtRef.setProp(dirStyle.getPropAt(i),dirStyle[i])
end repeat
-- if no line-height in default and current style, use "natural" line-height (font-size*1.3)
if voidP(pDefaultStyle.findPos("line-height")) AND voidP(dirStyle.findPos(#fixedLineSpace)) then
txtRef.setProp(#fixedLineSpace,txtRef.fontsize*1.3)
end if
end
----------------------------------------
-- Search & Replace with RegExp (global)
----------------------------------------
on _regReplace (me, str, pat, rep)
if pPRegExFlag then
l=[str]
PRegEx_Replace(l,pat,"g", rep)
return l[1]
else
return _js_RegExReplace(str,pat,rep)
end if
end
Contact
MMI
36 South Court Sq
Suite 300
Newnan, GA 30263
USA