<commit_before>% vim: sw=4 ts=4 et ft=erlang
-ifndef(wf_inc).
-define(wf_inc, ok).
-include("compat.hrl").
-include("wf_test.hrl").
-compile(nowarn_export_all).

%% This is the parse_transform to allow extending fields
-compile({parse_transform, rekt}).

-define(WF_EXTEND(OrigRec, NewRec, Module, Fields), -extend({OrigRec, NewRec, [{module, Module} | Fields]})).


%%% TYPES FOR DIALYZER %%%

%% Allow Dialzer to be run on the .ebin files
-compile(debug_info).

-type nitrogen_element()    :: tuple().
-type template_script_element() :: script | mobile_script.
-type body_element()        :: nitrogen_element() | binary() | string() | iolist() 
                                | template_script_element().
-type body()                :: body_element() | [body_element()].
-type action_element()      :: undefined | tuple() | string() | binary() | iolist().
-type actions()             :: action_element() | [action_element()].
-type validator_element()   :: nitrogen_element().
-type validators()          :: validator_element() | [validator_element()].
-type tag()                 :: term().
-type id()                  :: atom() | string() | binary().
-type proplist()            :: [{term(), term()}].
-type data_field_name()     :: atom() | text().
-type data_field_value()    :: atom() | text().
-type data_fields()         :: [data_field_name() 
                                | {data_field_name()} 
                                | {data_field_name(),data_field_value()}].
-type wire_priority()       :: eager | normal | defer.
-type class()               :: string() | binary() | atom().
-type text()                :: string() | binary() | iolist().
-type html_encode()         :: boolean() | whites | fun((term()) -> text()).
-type html()                :: string() | binary() | iolist().
-type script()              :: string() | binary() | iolist().
-type url()                 :: text() | atom().
-type path()                :: string() | binary().
-type html_name()           :: string() | binary() | atom().
-type mobile_theme()        :: string() | binary() | atom().
-type comet_name()          :: term().
-type comet_restart_msg()   :: term().
-type comet_function()      :: pid() | function() 
                                | {comet_name(), function()}
                                | {comet_name(), function(), comet_restart_msg()}.
-type handler_config()      :: any().
-type handler_state()       :: any().

-type google_chart_type()   :: line | sparkline | stacked_horizontal_bar
                                | stacked_vertical_bar | grouped_horizontal_bar
                                | grouped_vertical_bar | pie | pie3d.
-type color()               :: string() | binary() | atom().
-type google_chart_position()      :: top | left | bottom | right.
-type module_function()     :: {atom(), atom()}.
-type encoding_function()   :: module_function() | fun((iolist()) -> iolist()).
-type encoding()            :: none | unicode | auto | encoding_function().
-type context_data()        :: iolist() | {file, Filename :: path()}
                                | {stream, Size :: integer(), fun()}.
-type context_type()        :: first_request | postback_request | static_file | postback_websocket | undefined.
%%% CONTEXT %%%

% Page Request Information.
-record(page_context, {
    series_id,   % A unique ID assigned to the first request which stays constant on repeated requests.
    module,      % The requested page module
    entry_point=main, % The entry point for the module. Either an atom name
                      % (for Module:Atom/0) or can be a function arity 0
    path_info,   % Any extra info passed with the request
    async_mode= comet % {poll, Interval}, comet, or {websocket, WebsocketConnectionPid}
}).

% Event Information. A serialized version of this record
% is sent by the browser when an event is called.
-record(event_context, {
    module,     % The module that should handle the event
    tag,        % An Erlang term that is passed along with the event
    type,       % The type of event postback, comet, continuation, upload
    anchor,     % The element id to which trigger and target are relative.
    validation_group, % The validation group that should be run when this event is fired.
    handle_invalid % When an invalidation evaluates to false, instead of silently not
                   % issuing a postback event, call Delegate:event_invalid with the same tag.
}).

% Handlers Context-
% Handlers are used to encapsulate Nitrogen's behaviour, and
% to allow other frameworks to substitute their own behaviour.
% These are set in wf_context:make_context/1
-record(handler_context, {
    name,       % The name of a handler. See wf_context for a list.
    module,     % A module that provides the logic for a handler. This can be substituted by your app.
    config,     % The config of the handler, set at the beginning of each request.
    state       % The state of the handler, serialized and maintained between postbacks in a series.
    %serialize=true % whether or not to serialize and deserialize this handler during requests. Usually, just the state handler needs to be serialized.
}).

-record(context, {
    % Transient Information
    type                    :: context_type(),
    bridge                  :: simple_bridge:bridge(),
    anchor=undefined        :: id(), 
    data=[]                 :: context_data(),
    encoding=auto           :: encoding(),
    action_queue=undefined  :: wf_action_queue:action_queue() | undefined,
    % These are all serialized, sent to the browser
    % and de-serialized on each request.
    page_context            :: undefined | #page_context{},
    event_context           :: undefined | #event_context{},
    handler_list            :: undefined | list()
}).

%%% LOGGING %%%
-ifndef(debug_print).
-define(debug_print, true).
-define(PRINT(Var), error_logger:info_msg("DEBUG: ~p:~p~n~p~n  ~p~n", [?MODULE, ?LINE, ??Var, Var])).
-define(LOG(Msg, Args), error_logger:info_msg(Msg, Args)).
-define(DEBUG, error_logger:info_msg("DEBUG: ~p:~p~n", [?MODULE, ?LINE])).
-endif.

%%% GUARDS %%%

-define (IS_STRING(Term),
    (is_list(Term) andalso Term /= [] andalso is_integer(hd(Term)))).

-define (IS_ACTION_PRIORITY(Priority),
    (Priority=:=normal orelse Priority=:=eager orelse Priority=:=defer)).

-define (PRIORITY_WIRE(Priority),
        (case Priority of
            eager   -> eager;
            defer   -> defer;
            normal  -> wire;
            wire    -> wire
        end)
    ).

%%% TERNARY IF AND VARIATIONS %%%
-define(WF_IF(Term,IfTrue,IfFalse),
    fun() ->
        %% This is wrapped in a fun to contain the leaky-case expression. It
        %% allows for nesting ?WF_IF calls. Though it obviously does incur the
        %% minor overhead of creating an anonymous function then executing it.
        case Term of
            %% We use the long "WF_IF_VALUE" variable to prevent the likelyhood
            %% of a variable naming clash. This will throw some matching or
            %% "shadowing" errors if, in a function, you define a variable
            %% called "WF_IF_VALUE" before calling ?WF_IF. Given that this is
            %% unlikely to happen, this is an acceptable compromise.
            WF_IF_VALUE when WF_IF_VALUE==false;
                         WF_IF_VALUE==undefined;
                         WF_IF_VALUE==[] ->
                IfFalse;
            _ -> 
                IfTrue
        end
    end()).

-define(WF_IF(Term,IfTrue), ?WF_IF(Term,IfTrue,"")).

%%% FRAMEWORK %%%

%%% Elements %%%
-define(ELEMENT_BASE(Module),
        %% attribute is_element should only ever be the atom `is_element` but
        %% because Nitrogen copies the base element when it needs to, it's
        %% possible it might accidentally copy the wrong kind of element that
        %% it may think is an element but its not. In such an event, we allow
        %% any() here, so that it passes the dialyzer warning, and will throw
        %% an error appropriately during runtime.
        %%
        %% If we kept it as just `is_element`, that may be just fine, but then
        %% users would probably have to use dialyzer to debug the "is not an
        %% element" error
        is_element=is_element   :: is_element | any(), 
        module=Module           :: atom(),
        id                      :: id(),
        anchor                  :: id(),
        actions                 :: actions(),
        show_if=true            :: boolean(),
        class=""                :: class() | [class()],
        style=""                :: text(),
        html_id=""              :: id(),
        title=""                :: undefined | text(),
        data_fields=[]          :: data_fields()        
    ).

-record(elementbase, {?ELEMENT_BASE(undefined)}).
-record(template, {?ELEMENT_BASE(element_template),
       file                     :: string(),
       module_aliases=[]        :: [{atom(), atom()}],
       bindings=[]              :: proplist()
    }).
-record(function_el, {?ELEMENT_BASE(element_function),
        function=fun() ->[]end  :: fun() | [fun()]
    }).
-record(body, {?ELEMENT_BASE(element_body),
        body=[]                 :: body()
    }).

-define(H_ELEMENT(Size), {?ELEMENT_BASE(element_h),
        text=""                 :: text(),
        body=[]                 :: body(),
        html_encode=true        :: html_encode(),
        size=Size               :: undefined | integer()
    }).
-record(h,  ?H_ELEMENT(undefined)).
-record(h1, ?H_ELEMENT(1)).
-record(h2, ?H_ELEMENT(2)).
-record(h3, ?H_ELEMENT(3)).
-record(h4, ?H_ELEMENT(4)).
-record(h5, ?H_ELEMENT(5)).
-record(h6, ?H_ELEMENT(6)).

-record(list, {?ELEMENT_BASE(element_list),
        numbered=false          :: boolean(),
        body=[]                 :: body()
    }).
-record(listitem, {?ELEMENT_BASE(element_listitem),
        body=[]                 :: body(),
        text=""                 :: text(),
        html_encode=true        :: html_encode(),
        role=""                 :: term()
    }).
-record(br, {?ELEMENT_BASE(element_br) }).
-record(hr, {?ELEMENT_BASE(element_hr) }).
-record(p, {?ELEMENT_BASE(element_p),
        body=[]                 :: body(),
        text=""                 :: text(),
        html_encode=true        :: html_encode()
    }).
-record(label, {?ELEMENT_BASE(element_label),
        body=[]                 :: body(),
        text=""                 :: text(),
        html_encode=true        :: html_encode(),
        for=""                  :: id()
    }).
-record(pre, {?ELEMENT_BASE(element_pre),
        text=""                 :: text(),
        html_encode=true        :: html_encode()
     }).
-record(strong, {?ELEMENT_BASE(element_strong),
        body=""                 :: body(),
        text=""                 :: text(),
        html_encode=true        :: html_encode()
    }).
-record(em, {?ELEMENT_BASE(element_em),
        body=""                 :: body(),
        text=""                 :: text(),
        html_encode=true        :: html_encode()
    }).
-record(value, {?ELEMENT_BASE(element_value),
        text=""                 :: text(),
        html_encode=true        :: html_encode()
    }).
-record(link, {?ELEMENT_BASE(element_link),
        text=""                 :: text(),
        body=""                 :: body(),
        image=undefined         :: undefined | url(),
        new=false               :: boolean(),
        html_encode=true        :: html_encode(),
        mobile_target=false     :: boolean(),
        mobile_dialog=false     :: boolean(),
        url="javascript:"       :: script() | url(),
        click                   :: actions(),
        postback                :: term(),
        handle_invalid=false    :: boolean(),
        on_invalid              :: undefined | actions(),
        delegate                :: module()
    }).
-record(email_link, {?ELEMENT_BASE(element_email_link),
        text=""                 :: text(),
        body=""                 :: body(),
        html_encode=true        :: html_encode(),
        email=""                :: text()
    }).
-record(error, {?ELEMENT_BASE(element_error),
        text=""                 :: text(),
        html_encode=true        :: html_encode()
    }).
-record(span, {?ELEMENT_BASE(element_span),
        body=""                 :: body(),
        text=""                 :: text(),
        html_encode=true        :: html_encode()
    }).
-record(button, {?ELEMENT_BASE(element_button),
        text=""                 :: text(),
        body=""                 :: body(),
        image=undefined         :: undefined | url(),
        html_encode=true        :: html_encode(),
        next                    :: id(),
        click                   :: actions(),
        enter_clicks=[]         :: [id()],
        postback                :: term(),
        disabled=false          :: boolean(),
        handle_invalid=false    :: boolean(),
        on_invalid              :: undefined | actions(),
        delegate                :: module()
    }).
-record(literal, {?ELEMENT_BASE(element_literal),
        text=""                 :: text(),
        html_encode=true        :: html_encode()
    }).
-record(textbox, {?ELEMENT_BASE(element_textbox),
        text=""                 :: text(),
        maxlength=""            :: integer() | string(),
        size=""                 :: integer() | string(),
        placeholder=""          :: text(),
        html_encode=true        :: html_encode(),
        disabled=false          :: boolean(),
        readonly=false          :: boolean(),
        next                    :: id(),
        postback                :: term(),
        handle_invalid=false    :: boolean(),
        on_invalid              :: undefined | actions(),
        delegate                :: module(),
        html_name               :: html_name(),
        type=text               :: string() | atom(),
	autocomplete="off"      :: string() | atom()
    }).
-record(datepicker_textbox, {?ELEMENT_BASE(element_datepicker_textbox),
        text=""                 :: text(),
        maxlength=""            :: integer() | string(),
        size=""                 :: integer() | string(),
        placeholder=""          :: text(),
        html_encode=true        :: html_encode(),
        readonly=false          :: boolean(),
        disabled=false          :: boolean(),
        next                    :: id(),
        handle_invalid=false    :: boolean(),
        on_invalid              :: undefined | actions(),
        html_name               :: html_name(),
        validators=[]           :: validators(),
        options = [{dateFormat, "yy-mm-dd"}]    :: proplist()
    }).
-record(textbox_autocomplete, {?ELEMENT_BASE(element_textbox_autocomplete),
        tag                     :: term(),
        text=""                 :: text(),
        minLength=2             :: integer(),
        size=""                 :: integer() | string(),
        html_encode=true        :: html_encode(),
        placeholder=""          :: text(),
        disabled=false          :: boolean(),
        readonly=false          :: boolean(),
        delay=300               :: integer(),
        next                    :: id(),
        postback                :: term(),
        handle_invalid=false    :: boolean(),
        on_invalid              :: undefined | actions(),
        delegate                :: module()
    }).
-record(hidden, {?ELEMENT_BASE(element_hidden),
        text=""                 :: text(),
        html_encode=true        :: html_encode(),
        html_name               :: html_name(),
        disabled=false          :: boolean()
    }).
-record(textarea, {?ELEMENT_BASE(element_textarea),
        text=""                 :: text(),
        placeholder=""          :: text(),
        disabled=false          :: boolean(),
        readonly=false          :: boolean(),
        trap_tabs=false         :: boolean(),
        next                    :: id(),
        columns                 :: undefined | integer(),
        rows                    :: undefined | integer(),
        html_encode=true        :: html_encode(),
        html_name               :: html_name()
    }).
-record(range, {?ELEMENT_BASE(element_range),
        min=0                   :: integer(),
        max=100                 :: integer(),
        step=1                  :: integer(),
        value=0                 :: integer(),
        next                    :: id(),
        postback                :: term(),
        handle_invalid=false    :: boolean(),
        on_invalid              :: undefined | actions(),
        delegate                :: module()
    }).

-record(option, {
        text=""                 :: text(),
        value=undefined         :: text() | atom() | undefined,
        selected                :: boolean() | undefined,
        show_if=true            :: boolean(),
        disabled=false          :: boolean()
    }).

-type short_option()        :: {text(), text()} | text().
-record(option_group, {
        text=""                 :: text(),
        options=[]              :: [#option{} | short_option()],
        show_if=true            :: boolean(),
        disabled=false          :: boolean()
    }).

-type options()             :: [#option{} | short_option()] | [#option_group{}].
-record(dropdown, {?ELEMENT_BASE(element_dropdown),
        options=[]              :: undefined | options(),
        size=auto               :: auto | integer(),
        html_encode=true        :: html_encode(),
        postback                :: term(),
        handle_invalid=false    :: boolean(),
        on_invalid              :: undefined | actions(),
        delegate                :: module(),
        value                   :: atom() | text() | integer(),
        next                    :: id(),
        multiple=false          :: boolean(),
        disabled=false          :: boolean(),
        html_name               :: html_name()
    }).

-record(checkbox, {?ELEMENT_BASE(element_checkbox),
        text=""                 :: text(),
        label_position='after'  :: 'after' | before | none,
        html_encode=true        :: html_encode(),
        checked=false           :: boolean(),
        value="on"              :: text(),
        next                    :: id(),
        postback                :: term(),
        handle_invalid=false    :: boolean(),
        disabled=false          :: boolean(),
        on_invalid              :: undefined | actions(),
        delegate                :: module(),
        html_name               :: html_name()
    }).
-record(radiogroup, {?ELEMENT_BASE(element_radiogroup),
        body=[]                 :: body()
    }).
-record(radio, {?ELEMENT_BASE(element_radio),
        body=""                 :: body(),
        text=""                 :: text(),
        html_encode=true        :: html_encode(),
        value                   :: text(),
        next                    :: id(),
        name                    :: html_name(),
        checked=false           :: boolean(),
        postback                :: term(),
        handle_invalid=false    :: boolean(),
        on_invalid              :: undefined | actions(),
        delegate                :: module(),
        html_name               :: html_name()
    }).
-record(password, {?ELEMENT_BASE(element_password),
        text=""                 :: text(),
        maxlength=""            :: integer() | string(),
        size=""                 :: integer | string(),
        placeholder=""          :: text(),
        html_encode=true        :: html_encode(),
        disabled=false          :: boolean(),
        readonly=false          :: boolean(),
        next                    :: id(),
        postback                :: term(),
        handle_invalid=false    :: boolean(),
        on_invalid              :: undefined | actions(),
        delegate                :: module(),
        html_name               :: html_name()
    }).
-record(restful_form, {?ELEMENT_BASE(element_restful_form),
        method="POST"           :: string() | atom(),
        action                  :: url(),
        html_name               :: html_name(),
        target                  :: string() | atom(),
        enctype                 :: text(),
        body=[]                 :: body()
      }).
-record(restful_submit, {?ELEMENT_BASE(element_restful_submit),
        text="Submit"           :: text(),
        body=[]                 :: body(),
        html_encode=true        :: html_encode(),
        html_name               :: html_name()
    }).
-record(restful_reset, {?ELEMENT_BASE(element_restful_reset),
        text="Cancel"           :: text(),
        html_encode=true        :: html_encode(),
        html_name               :: html_name()
    }).
-record(restful_upload, {?ELEMENT_BASE(element_restful_upload),
        html_encode=true        :: html_encode(),
        html_name               :: html_name()
    }).
-record(panel, {?ELEMENT_BASE(element_panel),
        body=[]                 :: body(),<commit_msg>Some dialyzer fixers for restful_form<commit_after>% vim: sw=4 ts=4 et ft=erlang
-ifndef(wf_inc).
-define(wf_inc, ok).
-include("compat.hrl").
-include("wf_test.hrl").
-compile(nowarn_export_all).

%% This is the parse_transform to allow extending fields
-compile({parse_transform, rekt}).

-define(WF_EXTEND(OrigRec, NewRec, Module, Fields), -extend({OrigRec, NewRec, [{module, Module} | Fields]})).


%%% TYPES FOR DIALYZER %%%

%% Allow Dialzer to be run on the .ebin files
-compile(debug_info).

-type nitrogen_element()    :: tuple().
-type template_script_element() :: script | mobile_script.
-type body_element()        :: nitrogen_element() | binary() | string() | iolist() 
                                | template_script_element().
-type body()                :: body_element() | [body_element()].
-type action_element()      :: undefined | tuple() | string() | binary() | iolist().
-type actions()             :: action_element() | [action_element()].
-type validator_element()   :: nitrogen_element().
-type validators()          :: validator_element() | [validator_element()].
-type tag()                 :: term().
-type id()                  :: atom() | string() | binary().
-type proplist()            :: [{term(), term()}].
-type data_field_name()     :: atom() | text().
-type data_field_value()    :: atom() | text().
-type data_fields()         :: [data_field_name() 
                                | {data_field_name()} 
                                | {data_field_name(),data_field_value()}].
-type wire_priority()       :: eager | normal | defer.
-type class()               :: string() | binary() | atom().
-type text()                :: string() | binary() | iolist().
-type html_encode()         :: boolean() | whites | fun((term()) -> text()).
-type html()                :: string() | binary() | iolist().
-type script()              :: string() | binary() | iolist().
-type url()                 :: text() | atom().
-type path()                :: string() | binary().
-type html_name()           :: string() | binary() | atom().
-type mobile_theme()        :: string() | binary() | atom().
-type comet_name()          :: term().
-type comet_restart_msg()   :: term().
-type comet_function()      :: pid() | function() 
                                | {comet_name(), function()}
                                | {comet_name(), function(), comet_restart_msg()}.
-type handler_config()      :: any().
-type handler_state()       :: any().

-type google_chart_type()   :: line | sparkline | stacked_horizontal_bar
                                | stacked_vertical_bar | grouped_horizontal_bar
                                | grouped_vertical_bar | pie | pie3d.
-type color()               :: string() | binary() | atom().
-type google_chart_position()      :: top | left | bottom | right.
-type module_function()     :: {atom(), atom()}.
-type encoding_function()   :: module_function() | fun((iolist()) -> iolist()).
-type encoding()            :: none | unicode | auto | encoding_function().
-type context_data()        :: iolist() | {file, Filename :: path()}
                                | {stream, Size :: integer(), fun()}.
-type context_type()        :: first_request | postback_request | static_file | postback_websocket | undefined.
%%% CONTEXT %%%

% Page Request Information.
-record(page_context, {
    series_id,   % A unique ID assigned to the first request which stays constant on repeated requests.
    module,      % The requested page module
    entry_point=main, % The entry point for the module. Either an atom name
                      % (for Module:Atom/0) or can be a function arity 0
    path_info,   % Any extra info passed with the request
    async_mode= comet % {poll, Interval}, comet, or {websocket, WebsocketConnectionPid}
}).

% Event Information. A serialized version of this record
% is sent by the browser when an event is called.
-record(event_context, {
    module,     % The module that should handle the event
    tag,        % An Erlang term that is passed along with the event
    type,       % The type of event postback, comet, continuation, upload
    anchor,     % The element id to which trigger and target are relative.
    validation_group, % The validation group that should be run when this event is fired.
    handle_invalid % When an invalidation evaluates to false, instead of silently not
                   % issuing a postback event, call Delegate:event_invalid with the same tag.
}).

% Handlers Context-
% Handlers are used to encapsulate Nitrogen's behaviour, and
% to allow other frameworks to substitute their own behaviour.
% These are set in wf_context:make_context/1
-record(handler_context, {
    name,       % The name of a handler. See wf_context for a list.
    module,     % A module that provides the logic for a handler. This can be substituted by your app.
    config,     % The config of the handler, set at the beginning of each request.
    state       % The state of the handler, serialized and maintained between postbacks in a series.
    %serialize=true % whether or not to serialize and deserialize this handler during requests. Usually, just the state handler needs to be serialized.
}).

-record(context, {
    % Transient Information
    type                    :: context_type(),
    bridge                  :: simple_bridge:bridge(),
    anchor=undefined        :: id(), 
    data=[]                 :: context_data(),
    encoding=auto           :: encoding(),
    action_queue=undefined  :: wf_action_queue:action_queue() | undefined,
    % These are all serialized, sent to the browser
    % and de-serialized on each request.
    page_context            :: undefined | #page_context{},
    event_context           :: undefined | #event_context{},
    handler_list            :: undefined | list()
}).

%%% LOGGING %%%
-ifndef(debug_print).
-define(debug_print, true).
-define(PRINT(Var), error_logger:info_msg("DEBUG: ~p:~p~n~p~n  ~p~n", [?MODULE, ?LINE, ??Var, Var])).
-define(LOG(Msg, Args), error_logger:info_msg(Msg, Args)).
-define(DEBUG, error_logger:info_msg("DEBUG: ~p:~p~n", [?MODULE, ?LINE])).
-endif.

%%% GUARDS %%%

-define (IS_STRING(Term),
    (is_list(Term) andalso Term /= [] andalso is_integer(hd(Term)))).

-define (IS_ACTION_PRIORITY(Priority),
    (Priority=:=normal orelse Priority=:=eager orelse Priority=:=defer)).

-define (PRIORITY_WIRE(Priority),
        (case Priority of
            eager   -> eager;
            defer   -> defer;
            normal  -> wire;
            wire    -> wire
        end)
    ).

%%% TERNARY IF AND VARIATIONS %%%
-define(WF_IF(Term,IfTrue,IfFalse),
    fun() ->
        %% This is wrapped in a fun to contain the leaky-case expression. It
        %% allows for nesting ?WF_IF calls. Though it obviously does incur the
        %% minor overhead of creating an anonymous function then executing it.
        case Term of
            %% We use the long "WF_IF_VALUE" variable to prevent the likelyhood
            %% of a variable naming clash. This will throw some matching or
            %% "shadowing" errors if, in a function, you define a variable
            %% called "WF_IF_VALUE" before calling ?WF_IF. Given that this is
            %% unlikely to happen, this is an acceptable compromise.
            WF_IF_VALUE when WF_IF_VALUE==false;
                         WF_IF_VALUE==undefined;
                         WF_IF_VALUE==[] ->
                IfFalse;
            _ -> 
                IfTrue
        end
    end()).

-define(WF_IF(Term,IfTrue), ?WF_IF(Term,IfTrue,"")).

%%% FRAMEWORK %%%

%%% Elements %%%
-define(ELEMENT_BASE(Module),
        %% attribute is_element should only ever be the atom `is_element` but
        %% because Nitrogen copies the base element when it needs to, it's
        %% possible it might accidentally copy the wrong kind of element that
        %% it may think is an element but its not. In such an event, we allow
        %% any() here, so that it passes the dialyzer warning, and will throw
        %% an error appropriately during runtime.
        %%
        %% If we kept it as just `is_element`, that may be just fine, but then
        %% users would probably have to use dialyzer to debug the "is not an
        %% element" error
        is_element=is_element   :: is_element | any(), 
        module=Module           :: atom(),
        id                      :: id(),
        anchor                  :: id(),
        actions                 :: actions(),
        show_if=true            :: boolean(),
        class=""                :: class() | [class()],
        style=""                :: text(),
        html_id=""              :: id(),
        title=""                :: undefined | text(),
        data_fields=[]          :: data_fields()        
    ).

-record(elementbase, {?ELEMENT_BASE(undefined)}).
-record(template, {?ELEMENT_BASE(element_template),
       file                     :: string(),
       module_aliases=[]        :: [{atom(), atom()}],
       bindings=[]              :: proplist()
    }).
-record(function_el, {?ELEMENT_BASE(element_function),
        function=fun() ->[]end  :: fun() | [fun()]
    }).
-record(body, {?ELEMENT_BASE(element_body),
        body=[]                 :: body()
    }).

-define(H_ELEMENT(Size), {?ELEMENT_BASE(element_h),
        text=""                 :: text(),
        body=[]                 :: body(),
        html_encode=true        :: html_encode(),
        size=Size               :: undefined | integer()
    }).
-record(h,  ?H_ELEMENT(undefined)).
-record(h1, ?H_ELEMENT(1)).
-record(h2, ?H_ELEMENT(2)).
-record(h3, ?H_ELEMENT(3)).
-record(h4, ?H_ELEMENT(4)).
-record(h5, ?H_ELEMENT(5)).
-record(h6, ?H_ELEMENT(6)).

-record(list, {?ELEMENT_BASE(element_list),
        numbered=false          :: boolean(),
        body=[]                 :: body()
    }).
-record(listitem, {?ELEMENT_BASE(element_listitem),
        body=[]                 :: body(),
        text=""                 :: text(),
        html_encode=true        :: html_encode(),
        role=""                 :: term()
    }).
-record(br, {?ELEMENT_BASE(element_br) }).
-record(hr, {?ELEMENT_BASE(element_hr) }).
-record(p, {?ELEMENT_BASE(element_p),
        body=[]                 :: body(),
        text=""                 :: text(),
        html_encode=true        :: html_encode()
    }).
-record(label, {?ELEMENT_BASE(element_label),
        body=[]                 :: body(),
        text=""                 :: text(),
        html_encode=true        :: html_encode(),
        for=""                  :: id()
    }).
-record(pre, {?ELEMENT_BASE(element_pre),
        text=""                 :: text(),
        html_encode=true        :: html_encode()
     }).
-record(strong, {?ELEMENT_BASE(element_strong),
        body=""                 :: body(),
        text=""                 :: text(),
        html_encode=true        :: html_encode()
    }).
-record(em, {?ELEMENT_BASE(element_em),
        body=""                 :: body(),
        text=""                 :: text(),
        html_encode=true        :: html_encode()
    }).
-record(value, {?ELEMENT_BASE(element_value),
        text=""                 :: text(),
        html_encode=true        :: html_encode()
    }).
-record(link, {?ELEMENT_BASE(element_link),
        text=""                 :: text(),
        body=""                 :: body(),
        image=undefined         :: undefined | url(),
        new=false               :: boolean(),
        html_encode=true        :: html_encode(),
        mobile_target=false     :: boolean(),
        mobile_dialog=false     :: boolean(),
        url="javascript:"       :: script() | url(),
        click                   :: actions(),
        postback                :: term(),
        handle_invalid=false    :: boolean(),
        on_invalid              :: undefined | actions(),
        delegate                :: module()
    }).
-record(email_link, {?ELEMENT_BASE(element_email_link),
        text=""                 :: text(),
        body=""                 :: body(),
        html_encode=true        :: html_encode(),
        email=""                :: text()
    }).
-record(error, {?ELEMENT_BASE(element_error),
        text=""                 :: text(),
        html_encode=true        :: html_encode()
    }).
-record(span, {?ELEMENT_BASE(element_span),
        body=""                 :: body(),
        text=""                 :: text(),
        html_encode=true        :: html_encode()
    }).
-record(button, {?ELEMENT_BASE(element_button),
        text=""                 :: text(),
        body=""                 :: body(),
        image=undefined         :: undefined | url(),
        html_encode=true        :: html_encode(),
        next                    :: id(),
        click                   :: actions(),
        enter_clicks=[]         :: [id()],
        postback                :: term(),
        disabled=false          :: boolean(),
        handle_invalid=false    :: boolean(),
        on_invalid              :: undefined | actions(),
        delegate                :: module()
    }).
-record(literal, {?ELEMENT_BASE(element_literal),
        text=""                 :: text(),
        html_encode=true        :: html_encode()
    }).
-record(textbox, {?ELEMENT_BASE(element_textbox),
        text=""                 :: text(),
        maxlength=""            :: integer() | string(),
        size=""                 :: integer() | string(),
        placeholder=""          :: text(),
        html_encode=true        :: html_encode(),
        disabled=false          :: boolean(),
        readonly=false          :: boolean(),
        next                    :: id(),
        postback                :: term(),
        handle_invalid=false    :: boolean(),
        on_invalid              :: undefined | actions(),
        delegate                :: module(),
        html_name               :: html_name(),
        type=text               :: string() | atom(),
	autocomplete="off"      :: string() | atom()
    }).
-record(datepicker_textbox, {?ELEMENT_BASE(element_datepicker_textbox),
        text=""                 :: text(),
        maxlength=""            :: integer() | string(),
        size=""                 :: integer() | string(),
        placeholder=""          :: text(),
        html_encode=true        :: html_encode(),
        readonly=false          :: boolean(),
        disabled=false          :: boolean(),
        next                    :: id(),
        handle_invalid=false    :: boolean(),
        on_invalid              :: undefined | actions(),
        html_name               :: html_name(),
        validators=[]           :: validators(),
        options = [{dateFormat, "yy-mm-dd"}]    :: proplist()
    }).
-record(textbox_autocomplete, {?ELEMENT_BASE(element_textbox_autocomplete),
        tag                     :: term(),
        text=""                 :: text(),
        minLength=2             :: integer(),
        size=""                 :: integer() | string(),
        html_encode=true        :: html_encode(),
        placeholder=""          :: text(),
        disabled=false          :: boolean(),
        readonly=false          :: boolean(),
        delay=300               :: integer(),
        next                    :: id(),
        postback                :: term(),
        handle_invalid=false    :: boolean(),
        on_invalid              :: undefined | actions(),
        delegate                :: module()
    }).
-record(hidden, {?ELEMENT_BASE(element_hidden),
        text=""                 :: text(),
        html_encode=true        :: html_encode(),
        html_name               :: html_name(),
        disabled=false          :: boolean()
    }).
-record(textarea, {?ELEMENT_BASE(element_textarea),
        text=""                 :: text(),
        placeholder=""          :: text(),
        disabled=false          :: boolean(),
        readonly=false          :: boolean(),
        trap_tabs=false         :: boolean(),
        next                    :: id(),
        columns                 :: undefined | integer(),
        rows                    :: undefined | integer(),
        html_encode=true        :: html_encode(),
        html_name               :: html_name()
    }).
-record(range, {?ELEMENT_BASE(element_range),
        min=0                   :: integer(),
        max=100                 :: integer(),
        step=1                  :: integer(),
        value=0                 :: integer(),
        next                    :: id(),
        postback                :: term(),
        handle_invalid=false    :: boolean(),
        on_invalid              :: undefined | actions(),
        delegate                :: module()
    }).

-record(option, {
        text=""                 :: text(),
        value=undefined         :: text() | atom() | undefined,
        selected                :: boolean() | undefined,
        show_if=true            :: boolean(),
        disabled=false          :: boolean()
    }).

-type short_option()        :: {text(), text()} | text().
-record(option_group, {
        text=""                 :: text(),
        options=[]              :: [#option{} | short_option()],
        show_if=true            :: boolean(),
        disabled=false          :: boolean()
    }).

-type options()             :: [#option{} | short_option()] | [#option_group{}].
-record(dropdown, {?ELEMENT_BASE(element_dropdown),
        options=[]              :: undefined | options(),
        size=auto               :: auto | integer(),
        html_encode=true        :: html_encode(),
        postback                :: term(),
        handle_invalid=false    :: boolean(),
        on_invalid              :: undefined | actions(),
        delegate                :: module(),
        value                   :: atom() | text() | integer(),
        next                    :: id(),
        multiple=false          :: boolean(),
        disabled=false          :: boolean(),
        html_name               :: html_name()
    }).

-record(checkbox, {?ELEMENT_BASE(element_checkbox),
        text=""                 :: text(),
        label_position='after'  :: 'after' | before | none,
        html_encode=true        :: html_encode(),
        checked=false           :: boolean(),
        value="on"              :: text(),
        next                    :: id(),
        postback                :: term(),
        handle_invalid=false    :: boolean(),
        disabled=false          :: boolean(),
        on_invalid              :: undefined | actions(),
        delegate                :: module(),
        html_name               :: html_name()
    }).
-record(radiogroup, {?ELEMENT_BASE(element_radiogroup),
        body=[]                 :: body()
    }).
-record(radio, {?ELEMENT_BASE(element_radio),
        body=""                 :: body(),
        text=""                 :: text(),
        html_encode=true        :: html_encode(),
        value                   :: text(),
        next                    :: id(),
        name                    :: html_name(),
        checked=false           :: boolean(),
        postback                :: term(),
        handle_invalid=false    :: boolean(),
        on_invalid              :: undefined | actions(),
        delegate                :: module(),
        html_name               :: html_name()
    }).
-record(password, {?ELEMENT_BASE(element_password),
        text=""                 :: text(),
        maxlength=""            :: integer() | string(),
        size=""                 :: integer | string(),
        placeholder=""          :: text(),
        html_encode=true        :: html_encode(),
        disabled=false          :: boolean(),
        readonly=false          :: boolean(),
        next                    :: id(),
        postback                :: term(),
        handle_invalid=false    :: boolean(),
        on_invalid              :: undefined | actions(),
        delegate                :: module(),
        html_name               :: html_name()
    }).
-record(restful_form, {?ELEMENT_BASE(element_restful_form),
        method="POST"           :: string() | atom(),
        action                  :: url() | undefined,
        html_name               :: html_name(),
        target                  :: string() | atom(),
        enctype                 :: text() | undefined,
        body=[]                 :: body()
      }).
-record(restful_submit, {?ELEMENT_BASE(element_restful_submit),
        text="Submit"           :: text(),
        body=[]                 :: body(),
        html_encode=true        :: html_encode(),
        html_name               :: html_name()
    }).
-record(restful_reset, {?ELEMENT_BASE(element_restful_reset),
        text="Cancel"           :: text(),
        html_encode=true        :: html_encode(),
        html_name               :: html_name()
    }).
-record(restful_upload, {?ELEMENT_BASE(element_restful_upload),
        html_encode=true        :: html_encode(),
        html_name               :: html_name()
    }).
-record(panel, {?ELEMENT_BASE(element_panel),
        body=[]                 :: body(),
{'fraction_non_alphanumeric': 0.14255349298568798, 'fraction_numerical': 0.0013225638845590667, 'mean_word_length': 1.6363862773177262, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 23, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 18, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3896009', 'n_tokens_mistral': 11131, 'n_tokens_neox': 10824, 'n_words': 2707}