tml-html

TML html support

npm install tml-html
20 downloads in the last month

tml-html(3) -- html types

SYNOPSIS

tml-html html types.

OPTIONS

SYNTAX

ENVIRONMENT

RETURN VALUES

STANDARDS

SECURITY CONSIDERATIONS

BUGS

HISTORY

AUTHOR

SEE ALSO

IMPLEMENTATION


Html

@purpose Html types;
@author Erin Phillips;
@version 0.0.4-2;
    @history 2013-04-30 EMP [0.0.4-1] add-tml-build;
    @history 2013-05-01 EMP [0.0.4-2] add-table-append-string;

resources

    ENC = tml-encoder [Encoder]
    TMLR = tml-runtime [TmlRuntime]
    CACHE = tml-cache [Cache]

locals

    shuffleList = TMLR::shuffleList
    encode = ENC.encode
    decode = ENC.decode

tags

    MINS15MILLIS = 900000;
    XHTML = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml">';

notes

@need N.1 tag-open attrs elements tag-close;
@need N.2 attributes and elements indexed;
@need N.3 collapse static attributes and elements;

@need META 

AUTHOR The author's name.

CACHE-CONTROL

HTTP 1.1. Allowed values = PUBLIC | PRIVATE | NO-CACHE | NO-STORE.

Public - may be cached in public shared caches Private - may only be cached in private cache no-Cache - may not be cached no-Store - may be cached but not archived

The directive CACHE-CONTROL:NO-CACHE indicates cached information should not be used and instead requests should be forwarded to the origin server. This directive has the same semantics as the PRAGMA:NO-CACHE. Clients SHOULD include both PRAGMA:NO-CACHE and CACHE-CONTROL:NO-CACHE when a no-cache request is sent to a server not known to be HTTP/1.1 compliant.

Also see EXPIRES. Note: It may be better to specify cache commands in HTTP than in META statements, where they can influence more than the browser, but proxies and other intermediaries that may cache information.

CONTENT-LANGUAGE Declares the primary natural language(s) of the document. May be used by search engines to categorize by language.

CONTENT-TYPE The HTTP content type may be extended to give the character set. It is recommended to always use this tag and to specify the charset.

COPYRIGHT A copyright statement.

DESCRIPTION

The text can be used when printing a summary of the document. The text should not contain any formatting information. Used by some search engines to describe your document. Particularly important if your document has very little text, is a frameset, or has extensive scripts at the top.

EXPIRES

The date and time after which the document should be considered expired. An illegal EXPIRES date, e.g. "0", is interpreted as "now". Setting EXPIRES to 0 may thus be used to force a modification check at each visit.

Web robots may delete expired documents from a search engine, or schedule a revisit. HTTP 1.1 (RFC 2068) specifies that all HTTP date/time stamps MUST be generated in Greenwich Mean Time (GMT) and in RFC 1123 format. RFC 1123 format = wkday "," SP date SP time SP "GMT"

wkday = (Mon, Tue, Wed, Thu, Fri, Sat, Sun) date = 2DIGIT SP month SP 4DIGIT day month year (e.g., 02 Jun 1982) time = 2DIGIT ":" 2DIGIT ":" 2DIGIT 00:00:00 - 23:59:59 month = (Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec)

KEYWORDS The keywords are used by some search engines to index your document in addition to words from the title and document body. Typically used for synonyms and alternates of title words. Consider adding frequent misspellings. e.g. heirarchy, hierarchy.

PRAGMA NO-CACHE This directive indicates cached information should not be used and instead requests should be forwarded to the origin server. This directive has the same semantics as the CACHE-CONTROL:NO-CACHE directive and is provided for backwards compatibility with HTTP/1.0. Clients SHOULD include both PRAGMA:NO-CACHE and CACHE-CONTROL:NO-CACHE when a no-cache request is sent to a server not known to be HTTP/1.1 compliant. HTTP/1.1 clients SHOULD NOT send the PRAGMA request-header. HTTP/1.1 caches SHOULD treat "PRAGMA:NO-CACHE" as if the client had sent "CACHE-CONTROL:NO-CACHE". Also see EXPIRES.

REFRESH Specifies a delay in seconds before the browser automatically reloads the document. Optionally, specifies an alternative URL to load, making this command useful for redirecting browsers to other pages.

ROBOTS CONTENT="ALL | NONE | NOINDEX | INDEX| NOFOLLOW | FOLLOW | NOARCHIVE default = empty = "ALL" "NONE" = "NOINDEX, NOFOLLOW"

The CONTENT field is a comma separated list: INDEX: search engine robots should include this page. FOLLOW: robots should follow links from this page to other pages. NOINDEX: links can be explored, although the page is not indexed. NOFOLLOW: the page can be indexed, but no links are explored. NONE: robots can ignore the page. NOARCHIVE: Google uses this to prevent archiving of the page. See http://www.google.com/bot.html

GOOGLEBOT In addition to the ROBOTS META Command above, Google supports a GOOGLEBOT command. With it, you can tell Google that you do not want the page archived, but allow other search engines to do so. If you specify this command, Google will not save the page and the page will be unavailable via its cache. See Google's FAQ.

;

@need QUALITY

Layout Simplicity, consistency, and focus. Contrast, balance, and repetition. Proximity, similarity, and good continuation. Critical elements stand out. Critical information appears toward top left of the page. Works for printing and at a variety of window sizes (e.g., 520 pixel maximum width of your design). Provides appropriate focal point, emphasis, and hierarchy of information.

Background lmage Can be compressed to a reasonable size. Aligns with the foreground images. Will tile appropriately.

Navigation Navigation is scalable. The most complex page can be developed using this framework. Proper page titles and link labels have been used.

TexUFonts The typeface matches the page style. The number of typefaces is limited. The use of typefaces, weights, and emphasis is limited. HTML text is aliased (jaggy) and presented in the expected font. Font size is flexible. Text links are underlined. Text links are different colors for visited and unvisited links. Body text, titles, and labels are legible.

Images A consisteni light source ts used. The compression of the mockup does not lose too much visual quality. The images are used to support the content of the page.

Color Color is used appropriately (e.g., for grouping, pop-out effects, and so forih)' Color is appropriate for dark, light, and grayscale monitor settings. Contrast is appropriate for dark, light, and grayscale monitor settings.

Client Requirements Required logos, fonts, and colors are included in the mockup. Page titles, button labels, and link names are accurate. Appropriate identifying images and marks are included. The client address is correct.

;

@need CSS

Header Elements base link meta style title script

Structural Elements br div h1..6 hr p

List Elements li ol ul

Text Formatting Elements a abbr address b bdo blink cite code comment del dfn em ins kbd marquee nobr noscript plaintext pre q rb rbc rp rt rtc ruby samp small span strong sub sup var wbr xmp

Form Elements button fieldset form input label legend optgroup option select textarea

Image and Media Elements area bgsound img map noembed object param

Table Elements caption col colgroup table tbody td tfoot th thead tr

Frame and Window Elements iframe

doctype
html

Document Elements head body

;

code

-- STATICS --;

var elemCache = new CACHE::Store('ELEM',800,1000,##MINS15MILLIS;)

pattern patTagWithId :n

    <<<
    ##type TagI:n(id$)
        extends Tag:n
        uses WithId @setId(id);
    >>>

pattern patTagWithClass :n

    <<<
    ##type TagC:n(name$)
        extends Tag:n
        uses WithClasses
        @setClass(name);
    >>>

pattern patTagWithIdClass :n

    <<<
    ##type TagIC:n(id$,name$)
        extends Tag:n
        uses WithId
        uses WithClasses
        @setId(id).setClass(name);
    >>>

pattern patTagWithAttrs :n

    <<<
    ##type TagA:n(k$,v$)
        extends Tag:n
        uses WithAttrs @setAttr(k,v);
    >>>

pattern patTagWithStyles :n

    <<<
    ##type TagS:n(s$,v$)
        extends Tag:n
        uses WithStyles @setStyle(s,v);
    >>>

@need ATTR There must never be two or more attributes on the same start tag whose names are a case-insensitive match for each other.;

@need ATTR Four representations of attributes: empty, unquoted, single, double;

    @usage area, base, br, col, command, embed, hr, img,
    input, keygen, link, meta, param, source, track, wbr;

    @usage @open and @close should be static;

service ElemService

method out -> $;

aspect WithId

method getPosition ->

    =@position;

method setPosition # -> this

    @position=a;

method setId $ -> this

    if @idContent
        unset @idContent;
    @id=a;

method outId -> $

    = coalesce= @idContent (con ' id="' @id '"');;

aspect WithClasses

    @classes={}
    @hasClasses=false;

method setClass $ -> this

    if @classContent
        unset @classContent;
    @classes.(a)=true
    @hasClasses=true;

method deleteClass $ -> this

    if @classContent
        unset @classContent;
    delete @classes.(a);

method setOnlyClass $ -> this

    if @classContent
        unset @classContent;
    @classes={}
    @classes.(a)=true
    @hasClasses=true;

method outClasses -> $

    =@classContent
    ifnot a
        a = ' class="'
        each-key @classes as name
          if gt name_i 0
            con= a ' ';;
          con= a name;;
        con= a '"';
        @classContent=a;;

method outLabelClasses -> $

    =@labelClassContent
    ifnot a
        a = ' class="'
        each-key @classes as name
          if gt name_i 0
            con= a ' ';;
          con= a name 'Label';;
        con= a '"';
        @labelClassContent=a;;

aspect WithAttrs

    @attrs={}
    @hasAttrs=false;

method setAttr $ $ -> this

    if @attrContent
        unset @attrContent;
    if (hasval @withAttrsDupAsId and eq$ @withAttrsDupAsId a)
        @setId(b);
    @attrs.(a)=b
    @hasAttrs=true;

method getAttr $ -> opt $

    =@attrs.(a);

method deleteAttr $ -> this

    if @attrContent
        unset @attrContent;
    delete @attrs.(a);

method outAttrs -> $

    =@attrContent
    ifnot a
        a=''
        each-key @attrs as name
            con= a ' ' name '="' @attrs.(name) '"';;
        @attrContent=a;;

method setDupAsId $ -> this

    @withAttrsDupAsId=a;

aspect WithStyles

    @styles={}
    @hasStyles=false;

method setStyle $ $ -> this

    if @styleContent
        unset @styleContent;
    @styles.(a)=b
    @hasStyles=true;

method deleteStyle $ -> this

    if @styleContent
        unset @styleContent;
    delete @styles.(a);

method outStyles -> $

    =@styleContent
    ifnot a 
        a=' style="'
        each-key @styles as name
            con= a name ':' @styles.(name) ';';;
        con= a '"';
        @styleContent=a;;

method hide -> this

    @setStyle('display','none');

method show -> this

    @deleteStyle('display');

aspect WithElemIndex

    @elemIndex={};

method addWithElemIndex ? -> this

    vars o
         p
         l=@elems
         i=@elemIndex
         id=a.id;

    if isa a #
        a = as-string a;

    if id 
        o=i.(id)
        if o
          p = o.getPosition()
          a.setPosition(p)
          i.(id)=a
          l[p]=a
        else
          a.setPosition(item-count l)
          i.(id)=a
          push l a;
        ;
    else
        if (@encodeText and isa a $)
          push l encode(a);
        else
          push l a;
        ;;;

method setUrl url$ opt q% -> this

    if q
        con= url '?';
        each-key q as k
          if gt k_i 0
            con= url '&';;
          con= url k '=' q.(k);
        ;;
    @setAttr(@withLinksAttr,url);

method setTargetBlank -> this

    @setAttr('target','_blank');

method clearTarget -> this

    @deleteAttr('target');

private type ElemBase

private type ElemScript

    extends ElemBase
    @content=@staticOpen;

operator << $ -> this

    con= @content a;;

method out -> $

    =con @content @staticClose;;

private type ElemScriptRef(path$,name$)

    extends ElemBase
    @content=con @staticOpen path name @staticClose;;

method out -> $

    =@content;

private type ElemVoid

    extends ElemBase

method out -> $

    =@staticOpen
    if @hasClasses con= a @outClasses();;
    if @hasAttrs con= a @outAttrs();;
    if @hasStyles con= a @outStyles();;
    con= a @staticClose;
    ;

private type Elem

    extends ElemBase
    @elems=[]
    @hasElems=false
    @selfClose=true
    @encodeText=false;

method addLineBreak -> this

    push @elems '<br/>';;

operator <?< * -> this

    shuffleList(a)
    each a as elem @addElem(elem);;

method addElem ? -> this

    @hasElems=true
    if @elemIndex
        @addWithElemIndex(a)
    else
        if isa a #
            a = as-string a;
        case-if a
          $
            if @encodeText
              push @elems encode(a);
            else
              push @elems a;;
          %
            push @elems a;;;;

method setOnlyElem ? -> this

    @hasElems=true
    if @elemIndex
        @elemIndex={}
        @addWithElemIndex(a)
    else
        @elems=[]
        if isa a #
            a = as-string a;
        push @elems a;;;

method mergeElems %Elem -> this

    each a.elems as elem
        @addElem(elem);
    if a.hasClasses
        each-key @classes as k @setClass(k);;
    if a.hasAttrs
        each-key @attrs as k @setAttr(k,@attrs.(k));;
    if a.hasStyles
        each-key @styles as k @setStyle(k,@attrs.(k));;;

method outElems -> $=''

    if @hasElems
        each @elems as elem 
          case-if elem
            $
              con= a elem;
            :ElemService
              con= a elem.out();
            %ElemMarker
              a=a;;;;

method out -> $

    @purpose <label> tag goes before input tag if lable on the left;
    if (@fieldLabel and @labelLeft)
        if @hasClasses
          a = con '<label for="' @id '"' @outLabelClasses() '>'
                  @fieldLabel
                  '</label>'
                  @staticOpen;
        else
          a = con '<label for="' @id '">' @fieldLabel '</label>'
                  @staticOpen;
        ;
    else
        a = @staticOpen;

    @purpose call a preprocessor if one is defined for this element;
    if hasval @prep__
        @prep__();

    if hasval @id
        con= a @outId();;

    if @hasClasses
        con= a @outClasses();;

    if @hasAttrs
        con= a @outAttrs();;

    if @hasStyles
        con= a @outStyles();;

    if @hasElems
        if @legend
          con= a '>' '<legend>' @legend '</legend>'
               @outElems()
               @staticClose;
        else
          con= a '>'
               @outElems()
               @staticClose;;
    else
        if @selfClose
          con= a '/>';
        else
          con= a '>' @staticClose;;;

    @purpose <label> tag goes after input tag if lable on the right;
    if @fieldLabel
        ifnot @labelLeft
          if @hasClasses
            con= a '<label for="' @id '"' @outLabelClasses() '>' @fieldLabel '</label>';
          else
            con= a '<label for="' @id '">' @fieldLabel '</label>';;;;

    ;

private type ElemMarker(id$)

    extends ElemBase
    uses WithId
    @setId(id);

private type ElemFlow

    extends Elem

operator << ? -> this

    case-if a
        $ # %ElemFlow %ElemText %ElemMarker
            @addElem(a);;

private type ElemText

    extends Elem

operator << ? -> this

    case-if a
    $ # %ElemText %ElemMarker
        @addElem(a);;

private type ElemMeta(name$,val$,key$='name')

    extends ElemVoid
    provides ElemService
    @content=con @staticOpen
        key '="' name '" content="'
        val '"' @staticClose;;

static staticOpen = '<meta '

static staticClose = '/>'

method out -> $ = @content;

-- TAGS --;

type TagBody

    extends Elem
    provides ElemService
    @postElems=[]
    @hasPostElems=false;

operator << ? -> this

    case-if a
        %ElemFlow %ElemText
          @addElem(a)
        %TagScript %TagScriptRef        
          @hasPostElems=true
          push @postElems a;;;

method out -> $

    =con '<body>' @outElems();

    vars t='';
    if (@hasPostElems)
        each @postElems as elem
          case-if elem
            $
                con= t elem;
            :ElemService
                con= t elem.out();
            %ElemMarker
                con= t '';;;;
    con= a t '</body>';
    ;

method mergeTagBody %TagBody -> this

    @mergeElems(a)
    if (a.hasPostElems)
        each a.postElems as elem
            (@me << elem);;;

type TagCss

    extends ElemScript
    provides ElemService

static staticOpen = ''

type TagCssRef(name$,path$='c/')

    extends ElemScriptRef(path,name)
    provides ElemService

static staticClose = '.css"/>'

type TagDiv

    extends ElemFlow
    provides ElemService
    @selfClose=false;

static staticOpen = '<div'

static staticClose = ''

render patTagWithId Div;

render patTagWithClass Div;

render patTagWithIdClass Div;

type TagForm extends ElemFlow

    uses WithId
    uses WithAttrs
    uses WithLinks('action')
    provides ElemService
    @setDupAsId('name')
    @selfClose=false;

static staticOpen = '<form'

static staticClose = ''

method setName $ -> this

    @setAttr('name',a);

method setGet -> this

    @setAttr('method','get');

method setPost -> this

    @setAttr('method','post');

method setMultiPart -> this

    @setAttr('enctype','multipart/form-data');

operator << %ElemFlow -> this

    @addElem(a);

type TagFormField extends ElemText

    uses WithId
    uses WithAttrs
    provides ElemService
    @labelLeft=true
    @setDupAsId('name');

static staticOpen = '<input'

static staticClose = ''

method setMaxLength # -> this

    @setAttr('maxlength',as-string a);

method setName $ -> this

    @setAttr('name',a);

method setWidth # -> this

    @setAttr('size',as-string a);

method setValue $ -> this

    @setAttr('value',a);

method setNameValue $ $ -> this

    @setAttr('name',a).setAttr('value',b);

method setLabel $ ->this

    @fieldLabel=a;

method setLabelRight ->this

    @labelLeft=false;

method setLabelLeft ->this

    @labelLeft=true;

method removeLabel $ ->this

    delete @fieldLabel;

method setCheck -> this

    @setAttr('checked','checked');

method clearCheck -> this

    @deleteAttr('checked');

method disable -> this

    @setAttr('disabled','disabled');

method enable -> this

    delete @disabled;

method setReadOnly -> this

    @setAttr('readonly','readonly');

type TagFormButton

    extends TagFormField
    @setAttr('type','button');

type TagFormCheck

    extends TagFormField
    @setAttr('type','checkbox');

type TagFormFile

    extends TagFormField
    @setAttr('type','file');

type TagFormHidden

    extends TagFormField
    @setAttr('type','hidden');

type TagFormImageSubmit

    extends TagFormField
    uses WithLinks('src')
    @setName('submit')
    @setAttr('type','image');

type TagFormPassword

    extends TagFormField
    @setAttr('type','password');

type TagFormFieldSet

    extends ElemFlow
    provides ElemService

static staticOpen = '<fieldset'

static staticClose = ''

method setLegend $ -> this @legend=a;

method removeLegend $ -> this delete @legend;

type TagFormRadio

    extends TagFormField
    @setDupAsId('value')
    @setAttr('type','radio');

method setValue $ -> this

    @id=a @TagFormField.setValue(a);

method setNameValue $ $ -> this

    @id=b @TagFormField.setNameValue(a,b);

method setLegend $ -> this

    @legend=a;

method removeLegend $ -> this

    delete @legend;

render patTagWithClass FormRadio;

type TagFormReset

    extends TagFormField
    @setName('reset').setAttr('type','reset');

type TagFormSubmit

    extends TagFormField
    @setName('submit').setAttr('type','submit');

type TagFormTextArea

    extends TagFormField
    @selfClose=false;

static staticOpen = '<textarea'

static staticClose = ''

method setDimensions rows# cols# -> this

    @setAttr('rows',as-string rows).setAttr('cols',as-string cols);

type TagFormSelect extends TagFormField

    @options={}
    @selfClose=false;

static staticOpen = '<select'

static staticClose = ''

method setDisplaySize rows# -> this

    @setAttr('size',as-string rows);

method setMultiple -> this

    @setAttr('multiple','multiple');

method setSingle -> this

    @deleteAttr('multiple');

method setOptions % -> this

    @options=a;

method setChoice $ -> this

    @choice=a;

method clearChoice -> this

    delete @choice;

method prep__ -> this

    vars s=''
         o=@options
         c=@choice
         v;

    each-key o as k
        v=o.(k)
        case-if v
          % 
            con= s '<optgroup label="' k '">'
            each-key v as k2
              if (eq$ c k2)
                con= s '<option selected="selected" value="' k2 '">' v.(k2) '</option>';
              else
                con= s '<option value="' k2 '">' v.(k2) '</option>';
              ;
            ;
            con= s '</optgroup>'
          $
            if (eq$ c k)
              con= s '<option selected="selected" value="' k '">' o.(k) '</option>';
            else
              con= s '<option value="' k '">' o.(k) '</option>';
            ;
        ;
    ;

    @setOnlyElem(s)
    ;

render patTagWithClass FormSelect;

type TagHead

    extends Elem
    provides ElemService

method setTitle site$ page$ -> this

    @title = <title>con site ' - ' page</title>;

operator << ? -> this

    case-if a
    %ElemMeta %TagCss %TagCssRef %TagScript %TagScriptRef
      @addElem(a);;

method out -> $

    =<head>con @title @outElems()</head>;

type TagHtml

    extends Elem
    provides ElemService
    @head = new TagHead()
    @body = new TagBody()
    @addElem(@head).addElem(@body);

operator << ? opt $ -> this

    case-if a
        %ElemMeta %TagCss %TagCssRef %TagScript
          (@head << a)
        %TagScriptRef
          if eq$ b 'head'
            (@head << a)
          else
            (@body << a)
          ;
        %ElemBase
          (@body << a);;

method mergeTagHtml %TagHtml -> this

    @head.mergeElems(a.head)
    @body.mergeTagBody(a.body);

method out -> $

    =con ##XHTML; @outElems() '</html>';;

method setTitle site$ page$ -> this

    @head.setTitle(site,page);

method addShowHide clickId$ targetId$ -> this

    if noval @script
        @script = new TagScript()
        (@body << @script);
    @script.addEvent(
        con '#' clickId, 'click'
        ,con "$('#" targetId "').toggle();return false;");

method addEvent sel$ ev$ todo$ -> this

    if noval @script
        @script = new TagScript()
        (@body << @script);
    @script.addEvent(sel,ev,todo);

type TagImage

    extends ElemText
    uses WithAttrs
    uses WithLinks('src')
    provides ElemService

static staticOpen = '<img'

static staticClose = ''

method setDimensions width# height# -> this

    @setAttr('width',as-string width)
        .setAttr('height',as-string height);

method setMap $ -> this

    @setAttr('usemap',con '#' a);

method setIsMap -> this

    @setAttr('ismap','ismap');

method setAlt $ -> this

    @setAttr('alt',a);

type TagMap extends ElemText

    uses WithAttrs
    uses WithId
    provides ElemService
    @setDupAsId('name');

static staticOpen = '<map'

static staticClose = ''

method setName $ -> this

    @setAttr('name',a);

operator << ? -> this

    case-if a
        %TagMapArea
          @addElem(a);;

type TagMapArea extends ElemText

    uses WithAttrs
    uses WithLinks('href')
    provides ElemService

static staticOpen = '<area'

static staticClose = ''

method setRect -> this

    @setAttr('shape','rect');

method setCircle -> this

    @setAttr('shape','circle');

method setPoly -> this

    @setAttr('shape','poly');

method setAlt $ -> this

    @setAttr('alt',a);

method setCoords * -> this

    @coords=a;

method prep__ -> this

    if hasval @coords
        @setAttr('coords',join @coords ',')
    else
        @deleteAttr('coords');;
    extends ElemText
    uses WithLinks('href')
    uses WithAttrs
    provides ElemService
    @selfClose=false;

static staticOpen = '<a'

static staticClose = ''

type TagMetaCache(durSeconds#)

    extends ElemMeta('cache-control'
        ,(con 'max-age=' durSeconds ', must-revalidate')
        ,'http-equiv')

type TagMetaNoCache

    extends ElemMeta('cache-control','no-cache','http-equiv')

type TagMetaContentType(val$='text/html; charset=UTF-8')

    extends ElemMeta('content-type',val,'http-equiv')

type TagMetaAuthor(val$)

    extends ElemMeta('author',val)

type TagMetaAppName(val$)

    extends ElemMeta('application-name',val)

type TagMetaDescription(val$)

    extends ElemMeta('description',val)

type TagMetaKeywords(vals$)

    extends ElemMeta('keywords',vals)

type TagPara

    extends ElemText
    provides ElemService

static staticOpen = '<p'

static staticClose = ''

render patTagWithClass Para;

type TagPre

    extends ElemText
    provides ElemService
    @encodeText=true;

static staticOpen = '<pre'

static staticClose = ''

type TagPara

    extends ElemText
    provides ElemService

static staticOpen = '<p'

static staticClose = ''

type TagQuote

    extends ElemText
    provides ElemService

static staticOpen = '<q'

static staticClose = ''

type TagScript

    extends ElemScript
    provides ElemService

static staticOpen = ''

method addEvent sel$ ev$ todo$ -> this

    (@me << con '$("' sel '").' ev '(function(){' todo '});');

method addDocEvent ev$ todo$ -> this

    (@me << con "$(document)." ev '(function(){' todo '});');

type TagScriptRef(name$,path$='j/')

    extends ElemScriptRef(path,name)
    provides ElemService

static staticOpen = '<script type="text/javascript" src="'

static staticClose = '.js">'

type TagSmall

    extends ElemText
    provides ElemService

static staticOpen = '<small'

static staticClose = ''

type TagUnderline

    extends ElemText
    provides ElemService

static staticOpen = '<u'

static staticClose = ''

type TagSubscript

    extends ElemText
    provides ElemService

static staticOpen = '<sub'

static staticClose = ''

type TagSuperscript

    extends ElemText
    provides ElemService

static staticOpen = '<sup'

static staticClose = ''

type TagSpan

    extends ElemText
    provides ElemService
    @selfClose=false;

static staticOpen = '<span'

static staticClose = ''

render patTagWithId Span;

render patTagWithClass Span;

type TagTable

    extends ElemText
    uses WithAttrs
    provides ElemService

static staticOpen = '<table'

static staticClose = ''

method setBorder -> this

    @setAttr('border','1');

method clearBorder -> this

    @deleteAttr('border');

method build * %=@me -> this

    vars row;
    each a as r
        row = new TagTableRow()
        each r as c
          (row << (new TagTableCell() << c));
        (b << row);;

method buildHead * -> this

    vars o = new TagTableHead();
    (@build(a,o) << o);

method buildBody * -> this

  vars o = new TagTableBody();
  (@build(a,o) << o);

method buildFoot * -> this

  vars o = new TagTableFoot();
  (@build(a,o) << o);

operator << ? -> this

    case-if a
        %TagTableRow %TagTableHead %TagTableBody %TagTableFoot %TagTableColGroup
            @addElem(a)
        $
          @addElem(
            (new TagTableRow() << 
              ((new TagTableCell()) << a)))
    ;;

operator << -> this

    vars row;
    each a as item
      case-if item
        %TagTableRow %TagTableHead %TagTableBody %TagTableFoot %TagTableColGroup
            @addElem(item)
        $
          if noval row
            row = new TagTableRow();
          (row << 
            ((new TagTableCell()) << item))
      ;
    ;
    if hasval row
      (@me << row);;

render patTagWithId Table;

render patTagWithClass Table;

type TagTableBody

    extends ElemText
    provides ElemService

static staticOpen = '<tbody'

static staticClose = ''

operator << ? -> this

    case-if a
        %TagTableRow
            @addElem(a);;

render patTagWithId TableBody;

render patTagWithClass TableBody;

type TagTableColGroup

    extends ElemText
    provides ElemService

static staticOpen = '<colgroup'

static staticClose = ''

operator << ? -> this

    case-if a
        %TagTableColGroupCol
            @addElem(a);;

render patTagWithId TableColGroup;

render patTagWithClass TableColGroup;

type TagTableColGroupCol

    extends ElemText
    uses WithAttrs
    uses WithStyles
    provides ElemService

static staticOpen = '<col'

static staticClose = ''

method setSpan # -> this

    @setAttr('span',as-string a);

render patTagWithId TableColGroupCol;

render patTagWithClass TableColGroupCol;

type TagTableHead extends ElemText provides ElemService

static staticOpen = '<thead'

static staticClose = ''

operator << ? -> this

    case-if a
        %TagTableRow
            @addElem(a)
          $
            @addElem(
              ((new TagTableHeaderCell()) << a)
              );;

operator << -> this

    each a as item
      case-if item
          %TagTableRow
            @addElem(item)
          $
            @addElem(
              ((new TagTableHeaderCell()) << item)
              )
          ;;;

render patTagWithId TableHead;

render patTagWithClass TableHead;

type TagTableFoot

    extends ElemText
    provides ElemService

static staticOpen = '<tfoot'

static staticClose = ''

operator << ? -> this

    case-if a
        %TagTableRow
            @addElem(a);;

render patTagWithId TableFoot;

render patTagWithClass TableFoot;

type TagTableRow

    extends ElemText
    provides ElemService

static staticOpen = '<tr'

static staticClose = ''

operator << ? -> this

    case-if a
        %TagTableCell %TagTableHeaderCell
            @addElem(a)
          $
            @addElem(
              ((new TagTableCell()) << a)
              );;

operator << -> this

    each a as item
      case-if item
          %TagTableCell %TagTableHeaderCell
            @addElem(item)
          $
            @addElem(
              ((new TagTableCell()) << item)
              )
          ;;;

render patTagWithId TableRow;

render patTagWithClass TableRow;

type TagTableCell

    extends ElemText
    uses WithAttrs
    provides ElemService

static staticOpen = '<td'

static staticClose = ''

method setColSpan # -> this

    @setAttr('colspan',as-string a);

method setRowSpan # -> this

    @setAttr('rowspan',as-string a);

render patTagWithId TableCell;

render patTagWithClass TableCell;

type TagTableHeaderCell

    extends ElemText
    uses WithAttrs
    provides ElemService

static staticOpen = '<th'

static staticClose = ''

method setColSpan # -> this

    @setAttr('colspan',as-string a);

method setRowSpan # -> this

    @setAttr('rowspan',as-string a);

render patTagWithId TableHeaderCell;

render patTagWithClass TableHeaderCell;

-- PAGES --;

type Site(@name$)

    extends TagHtml
    (@me << new TagMetaContentType());

method getName ->$=@name;

type Template(idList*)

    extends Elem
    uses WithElemIndex
    each idList as id
        @addElem(new ElemMarker(id));;

operator << ? -> this

    @addElem(a);

type Page(@name$,site%Site,template%Template)

    extends TagHtml
    @head.setTitle(site.name,@name)
    @mergeTagHtml(site)
    @body.mergeElems(template);

@need SITE master page; @need SITE title; @need SITE metadata;

@need SITE encaps page templates;

@need SITE render: pick a page, which is linked to ; @need SITE replaces indexed regions renders pages using available context;

@need LAYOUT ; @need RENDER to render a page

    SITE-RESPONSE = 
      (new PAGE.merge(SITE)
              .merge(
                LAYOUT.merge(WIDGET[].merge(CONTEXT))
                )).out()

    page(name,SITE) = Elem('html')
    page.head=new Head().setTitle(site.getName,name)
    page.body=new Body().merge(site.body).merge(template.body).mergeWigets(widgets)
    layout.merge(widget[])

    ;

-- TESTS --;

test ENCODE

test-eq : should encode special characters

    encode('<>') vs '&lt;&gt;'

test-eq : should decode special characters

    decode('&lt;&gt;') vs '<>'
    ;

test WITH_ID

test-eq : should build the id attribute

    (new WithId()).setId('99Hello').outId()
    vs
    ' id="99Hello"'
    ;

test WITH_ATTRS

test-eq : should build the attributes

    (new WithAttrs())
        .setAttr('display','none')
        .setAttr('color','red')
        .outAttrs()
    vs
    ' display="none" color="red"'
    ;

test WITH_CLASSES

test-eq : should build the classes

    (new WithClasses()).setClass('big').setClass('small').outClasses()
    vs
    ' class="big small"'       
    ;

test WITH_STYLES

test-eq : should build the styles

    (new WithStyles())
        .setStyle('display','none')
        .setStyle('color','red')
        .outStyles()
    vs
    ' style="display:none;color:red;"'
    ;        

test TAG_BODY

test-eq : should make a body tag with post-group js

    (new TagBody()
        << (new TagScript('OOFOO') << 'var f=9;')
        << (new TagSpan() << 'I vote.')).out()
    vs
    '<body><span>I vote.</span><script type="text/javascript">$(function(){var f=9;});</script></body>'
    ;

test TAG_CSS

test-eq : should build css tag

    (new TagCss() << 'h1 {color:red;}' << 'h2 {color:blue;}').out()
    vs
    '<style type="text/css">h1 {color:red;}h2 {color:blue;}</style>'
    ;

test TAG_CSSREF

test-eq : should build cssref tag

    (new TagCssRef('hello')).out()
    vs
    '<link rel="stylesheet" type="text/css" href="c/hello.css"/>'
    ;

test TAG_DIV

test-eq : should build div empty div tag

    (new TagDiv()).out() 
    vs
    '<div></div>'
    ;

test TAG_FORM

test-eq : should build a form

    (new TagForm()).setUrl('/submit').out()
    vs
    '<form action="/submit"></form>'
    ;

test TAG_HEAD

test-eq : should make a head tag

    (new TagHead()).setTitle('NEWSITE','NEWPAGE').out()
    vs
    '<head><title>NEWSITE - NEWPAGE</title></head>'

test-eq : should make a head tag with meta

    ((new TagHead()).setTitle('NEWSITE','NEWPAGE')
        << new TagMetaAppName('OOFOO')).out()
    vs
    '<head><title>NEWSITE - NEWPAGE</title><meta name="application-name" content="OOFOO"/></head>'
    ;

test TAG_ISPAN

test-eq : should build a span tag with id

    (new TagISpan('myId') << 'Hello').out()
    vs
    <span id="myId">'Hello'</span>
    ;
    ((new TagLink()).setUrl('http://google.com') << 'Google').out()
    vs
    '<a href="http://google.com">Google</a>'
    (new TagLink() << 'Google').setUrl('/',{a:0,b:'hello'}).out()
    vs
    '<a href="/?a=0&b=hello">Google</a>'
    ;

test TAG_META

test-eq : should build cache-control tag

    (new TagMetaCache(300)).out()
    vs
    '<meta http-equiv="cache-control" content="max-age=300, must-revalidate"/>'

test-eq : should build no-cache tag

    (new TagMetaNoCache()).out()
    vs
    '<meta http-equiv="cache-control" content="no-cache"/>'

test-eq : should build content-type tag

    (new TagMetaContentType()).out()
    vs
    '<meta http-equiv="content-type" content="text/html; charset=UTF-8"/>'

test-eq : should build author tag

    (new TagMetaAuthor('Erin Phillips')).out()
    vs
    '<meta name="author" content="Erin Phillips"/>'

test-eq : should build application-name tag

    (new TagMetaAppName('OOFOO')).out()
    vs
    '<meta name="application-name" content="OOFOO"/>'

test-eq : should build description tag

    (new TagMetaDescription('The voting platform')).out()
    vs
    '<meta name="description" content="The voting platform"/>'

test-eq : should build keywords tag

    (new TagMetaKeywords('vote,voot,vooter,oofoo')).out()
    vs
    '<meta name="keywords" content="vote,voot,vooter,oofoo"/>'
    ;

test TAG_SCRIPT

test-eq : should build script tag

    (new TagScript() << 'var f=function(){}').out()
    vs
    '<script type="text/javascript">$(function(){var f=function(){}});</script>'

test-eq : should set a tag event script

    (new TagScript()).addEvent('#tag','click','doit();').out()
    vs
    '<script type="text/javascript">$(function(){$("#tag").click(function(){doit();});});</script>'

test-eq : should set a document event script

    (new TagScript()).addDocEvent('click','doit();').out()
    vs
    '<script type="text/javascript">$(function(){$(document).click(function(){doit();});});</script>'
    ;

test TAG_SCRIPTREF

test-eq : should build scriptref tag

    (new TagScriptRef('Gui')).out()
    vs
    '<script type="text/javascript" src="j/Gui.js"></script>'
    ;

test TAG_SPAN

test-eq : should build a span tag

    (new TagSpan() << 'Hello').out()
    vs
    '<span>Hello</span>'
    ;

test TAG_TEMPLATE

test-eq : should render elems contained

    (new Template(['g1','g2'])
        << (new TagISpan('g2') << 'hello g2')
        << (new TagISpan('g1') << 'hello g1')).outElems()
    vs
    '<span id="g1">hello g1</span><span id="g2">hello g2</span>'
    ;

test TAG_TABLE

test-eq : should build a table using strings

    (new TagTable() << 'Hello').out()
    vs
    '<table><tr><td>Hello</td></tr></table>'
    ;
npm loves you