Skip to content

Instantly share code, notes, and snippets.

@jshirley
Created May 29, 2012 17:06

Revisions

  1. jshirley created this gist May 29, 2012.
    476 changes: 476 additions & 0 deletions macros.tt
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,476 @@
    [%~
    # Some illustrious documentation to get you started:
    #
    # The macros defined are to generate markup to save people from copy and pasting
    # markup. They copy and paste hashes instead, which should be safer (in theory)
    #
    # The macros for forms are:
    # * text_field - A simple text field, pass in type to override input type
    # * password_field - A password field, equivalent to
    # text_field({ type => 'password', ... })
    # * textarea_field - A textarea field
    # * readonly_field - A display-only field, with a hidden input
    # * select_field - A <select> field, with options passed in the `array`
    # parameter.
    # * checkboxes - A group of fields, displayed in nice fashion. If a
    # radio is needed, pass type => 'radio'.
    # * checkbox - A single checkbox field
    # If a radio is needed, pass type => 'radio'.
    # * form_actions - Use this method to cap off the form actions and end
    # a form.
    #
    # These use Twitter Bootstrap markup structure for the markup. Without the
    # bootstrap CSS, things will not look correct. You can read more about the
    # Bootstrap forms at http://twitter.github.com/bootstrap/base-css.html#forms
    #
    # The basic structure is a containing div and the label and input is in a
    # separate div.
    #
    # Additionally, there is a pretty_date formatter that will display dates
    # in a human friendly format. Things like, '24 minutes ago' versus a silly
    # date string.
    #
    # It is recommended to look at /guide/form to see this stuff in action.
    #
    # Integration with Catalyst::Plugin::MessageStack and Data::Manager. This again
    # is better demonstrated looking at /guide/form.
    #
    # To automatically look inside the `messages` and `results` objects in the
    # stash, simply set `context.scope` to the current scope of the results.
    # After that, messaging and sticky values should co-exist happily.

    USE time_dir = Time.Duration;


    MACRO system_localize(str) BLOCK;
    IF str.match('^\@'); c.loc(str); ELSE; str | html; END;
    END;

    MACRO pretty_date(dt) BLOCK;
    SET now_secs = now.epoch;
    SET then_secs = dt.epoch || now_secs;

    IF(now_secs > then_secs);
    time_dir.ago(now_secs - then_secs);
    ELSE;
    time_dir.from_now(then_secs - now_secs);
    END;
    END;

    MACRO form_actions(actions) BLOCK;
    IF !actions; actions = [ { } ]; END;
    %]
    <div class="form-actions">
    [% FOREACH action IN actions;
    IF action.type == 'cancel' %]
    <button class="btn"><i class="icon-remove"></i> [% c.loc(action.label || 'Cancel') %]</button>
    [% ELSE %]
    <button class="btn btn-primary"><i class="icon-[% action.icon || 'ok' %] icon-white"></i> [% c.loc(action.label || 'Save') %]</button>
    [% END %]
    [% END %]
    </div>[%
    END;

    MACRO label_field(info) BLOCK;
    IF !info.error && stack && stack.count;
    error_name = info.name;
    IF context.scope;
    error_name = error_name.replace("${context.scope}.", "");
    END;
    info.error = c.loc( stack.for_subject(error_name).for_level('error').first_message.id );
    END %]
    <label class="control-label" for="[% info.id %]"
    [%~ IF info.tooltip %] data-tooltip="[% info.tooltip | html %]"[% END ~%]
    >
    [%~ c.loc(info.label, info.params) ~%]
    </label>
    [% END;

    MACRO readonly_field(info) BLOCK;
    IF info.filter && info.value;
    f = info.filter;
    IF f == "join";
    info.value = info.value.join(', ');
    ELSE;
    TRY; info.value = FILTER $f; info.value; END; CATCH; "Error!"; END;
    END;
    END;
    %]
    <div class="control-group[% IF info.error %] error[% ELSIF info.warning %] warning[% ELSIF info.success %] success[% END %]">
    [% label_field(info) %]
    <div class="controls ro">
    <input type="hidden" id="form_[% info.name %]" name="[% info.name %]" value="[% info.value | html %]"/><span>
    <span class="uneditable-input">[% info.value %]</span>
    [% IF info.link || info.required || info.error || info.success || info.warning %]
    <span class="help-inline">
    [% IF info.required %]<span class="req">[% c.loc('FIELD REQUIRED SPLAT') %]</span>[% END; info.error; info.warning; info.success %]
    [%~ IF info.link %]<a href="[% info.link %]">[%~ END ~%]
    </span>
    [% END %]
    [% IF info.support %]<p class="help-block">[% c.loc(info.support) %]</p>[% END %]
    [%~ IF info.link ~%]</a>[%~ END ~%]</span>
    </div>
    </div>
    [% END;

    MACRO select_field(info) BLOCK;
    IF !info.defined('value_method'); info.value_method = 'value'; END;
    IF !info.defined('label_method'); info.label_method = 'label'; END;

    IF !info.value && results.${context.scope};
    value_name = info.name;
    IF context.scope; value_name = value_name.replace("${context.scope}.", ""); END;
    info.value = results.${context.scope}.get_original_value(value_name);
    END;
    IF !info.error && messages.for_scope(context.scope);
    value_name = info.name;
    IF context.scope; value_name = value_name.replace("^${context.scope}.", ""); END;
    info.error = messages.for_scope(context.scope).for_subject(value_name).first_message.id;
    END;
    %]
    [%~ IF info.dynamic_array =%]<script type="text/javascript">if ( typeof dynamic_forms === 'undefined' ) dynamic_forms = {}; dynamic_forms['[% info.value_from %]'] = { 'source': '[% info.dynamic_array %]', 'update': '[% info.name %]', 'value': "[% info.value || html %]" };</script>[% IF info.options.size == 0; info.options.push(c.loc('Please select [_1]', [ info.value_from ] ) ); END; END %]
    <div class="control-group[% IF info.error %] error[% ELSIF info.warning %] warning[% ELSIF info.success %] success[% END %]">
    [% label_field(info) %]
    <div class="select controls[% IF info.container_classes; " "; info.container_classes.join(" "); END %]">
    <[% IF info.type == 'radio'; "div"; ELSE; "select"; END %] id="form_[% info.name %]" name="[% info.name %]"
    [%~ IF info.classes %] class="[% info.classes.join(' ') %]"[%~ END ~%]
    [%~ IF info.disabled %] disabled="disabled"[%~ END ~%]
    [%~ FOREACH datum IN info.data.pairs %] data-[% datum.key %]="[% datum.value %]"[%~ END ~%]>
    [% IF info.type != "radio" && info.default_option %]
    [% IF info.type == 'radio' %]
    [% ELSE %]
    <option value="[% info.default_option.0 | html %]">[% info.default_option.1 %]</option>
    [% END %]
    [% END %]
    [% IF info.optgroups;
    info.options = [];
    DEFAULT info.children = 'children';
    FOREACH parent IN info.optgroups;
    info.options.push({ optgroup => parent.${info.label_method} });
    FOREACH child IN parent.children;
    info.options.push(child);
    END;
    END;
    END %]
    [% has_optgroup = 0;
    FOREACH item = info.options;
    IF item.optgroup; has_optgroup = 1;
    IF !loop.first %]</optgroup>[% END;
    %]<optgroup label="[% item.optgroup | html %]">[%
    ELSE;
    IF info.defined('value_method') && item.keys.defined;
    SET this_value = item.${info.value_method};
    ELSE;
    SET this_value = item;
    END;
    IF info.defined('label_method') && item.keys.defined;
    SET this_name = item.${info.label_method};
    ELSE;
    SET this_name = item;
    END;
    IF info.defined('elide');
    this_name = string_trunc.elide(this_name, info.elide);
    END;
    %][% IF info.type == "radio" %]<label><input type="radio" name="[% info.name %]" value="[% this_value | html %]"[% IF info.value == this_value %] checked="checked"[% END %]> [% c.loc(this_name) %]</label>[% info.radio_trailing %]
    [% ELSE %]<option value="[% this_value | html %]" [% IF this_value == info.value %]selected="selected"[% END %]>[% c.loc(this_name) %]</option>[% END %]
    [% END; # Not an optgroup %]
    [% END;
    IF has_optgroup %]</optgroup>[% END %]
    [% IF info.type != 'radio' %]</select>[% END %]
    [% IF info.type == 'radio' %]
    [% IF info.required %]<span class="req">[% c.loc('FIELD REQUIRED SPLAT') %]</span>[% END %]
    </div>
    [% ELSE %]
    [% IF info.required || info.error || info.success || info.warning %]
    [% END %]
    <span class="help-inline">[% IF info.required %]<span class="req">[% c.loc('FIELD REQUIRED SPLAT') %]</span>[% END; info.error; info.warning; info.success %]</span>
    [% END %]
    [% IF info.support %]<p class="help-block">[% c.loc(info.support) %]</p>[% END %]
    </div>
    </div>
    [% END;

    MACRO password_field(info) BLOCK;
    info.type = 'password';
    info.value = ''; # Force values to not be set.
    text_field(info);
    END;

    MACRO datetime_field(info) BLOCK;
    IF !info.defined('type'); info.type = 'date'; END;

    IF !info.defined('value');
    IF results.${context.scope};
    value_name = info.name;
    IF context.scope; value_name = value_name.replace("^${context.scope}.", ""); END;
    info.value = results.${context.scope}.get_original_value(value_name);
    END;
    IF !info.defined('value') && info.default_value;
    info.value = info.default_value;
    END;
    END;

    IF !info.error && messages.for_scope(context.scope);
    value_name = info.name;
    IF context.scope; value_name = value_name.replace("^${context.scope}.", ""); END;
    message = messages.for_scope(context.scope).for_subject(value_name).first_message;
    IF message; info.${message.level} = c.loc(message.id); END;
    END;

    IF info.filter && info.value;
    f = info.filter;
    IF f == "join";
    info.value = info.value.join(', ');
    ELSE;
    TRY; info.value = FILTER $f; info.value; END; CATCH; "Error applying filter to ${info.name}!"; END;
    END;
    END;

    IF info.defined('value') && info.defined('timezone');
    info.value = set_timezone(info.value, info.timezone);
    END;

    DEFAULT info.id = "form_" _ info.name.replace('\.', '_');
    %]
    <div class="control-group[% IF info.error %] error[% ELSIF info.warning %] warning[% ELSIF info.success %] success[% END %]">
    [% label_field(info) %]
    <div class="[% info.type || 'datetime' %] controls row">
    [%~ IF info.prepend || info.append ~%]<div class="[% IF info.prepend %]input-prepend[% END; IF info.append %] input-append[% END %]">
    [%~ IF info.prepend ~%]<span class="add-on">[% info.prepend %]</span>[%~ END ~%]
    [%~ END ~%]
    <input style="width: 90px;" [%~ ~%]
    type="[% info.type || 'date' %]"[%~ ~%]
    id="[% info.id %]" name="[% info.name %].date"[%~ =%]
    [% IF info.pattern %] pattern="[% info.pattern %]" [% END =%]
    value="[% info.value | html %]"
    [%~ IF info.classes %] class="[% info.classes.join(" ") %]"[% END ~%]
    [% # The unicode char below is purely to make the placeholder junk work. %]
    [%~ IF info.hint %] placeholder="&#x25E6; [% c.loc(info.hint) %]"[% END ~%]
    [%~ IF info.autocomplete == 0 %] autocomplete="false"[% END ~%]
    [%~ IF info.disabled == 1 %] disabled="disabled"[% END ~%]
    [%~ IF info.tabindex %] tabindex="[% info.tabindex %]"[% END ~%]
    [%~ IF info.maxlength %] maxlength="[% info.maxlength %]"[% END ~%]>
    [%~ IF info.append ~%]<span class="add-on">[% info.append %]</span>[%~ END ~%]
    [%~ IF info.prepend || info.append ~%]</div>[%~ END ~%]
    <select name="[% info.name %].hour" style="width: 5em;">
    [% FOREACH i IN [0..23] %]
    <option value="[% i | format("%02d") %]"[% IF info.value.hour && info.value.hour == i %] selected="selected"[% END %]>
    [% IF info.military_time %]
    [% i | format("%02d") %]
    [% ELSE;
    IF i == 0; c.loc("[_1]AM", 12);
    ELSIF i < 12; c.loc("[_1]AM", i);
    ELSIF i == 12; c.loc("[_1]PM", 12);
    ELSE; c.loc("[_1]PM", i - 12);
    END %]
    [% END %]
    </option>
    [% END %]
    </select>
    <select name="[% info.name %].minute" style="width: 5em;">
    [% FOR i IN [0, 15, 30, 45] %]
    <option value="[% i | format("%02d") %]"[% IF info.value.minute && info.value.minute >= i && info.value.minute < i + 15 %] selected="selected"[% END %]>[% i | format("%02d") %]</option>
    [% END %]
    </select>
    [% IF info.required || info.error || info.success || info.warning %]
    <span class="help-inline">[% IF info.required %]<span class="req">[% c.loc('FIELD REQUIRED SPLAT') %]</span>[% END; info.error; info.warning; info.success %]</span>
    [% END %]

    [% IF info.support %]<p class="help-block">[% c.loc(info.support) %]</p>[% END %]
    </div>
    </div>
    [% END;

    MACRO text_field(info) BLOCK;
    IF !info.defined('value');
    IF results.${context.scope};
    value_name = info.name;
    IF context.scope; value_name = value_name.replace("^${context.scope}.", ""); END;
    info.value = results.${context.scope}.get_original_value(value_name);
    END;
    IF !info.defined('value') && info.default_value;
    info.value = info.default_value;
    END;
    END;

    IF !info.error && messages.for_scope(context.scope);
    value_name = info.name;
    IF context.scope; value_name = value_name.replace("^${context.scope}.", ""); END;
    message = messages.for_scope(context.scope).for_subject(value_name).first_message;
    IF message; info.${message.level} = c.loc(message.id); END;
    END;

    IF info.filter && info.value;
    f = info.filter;
    IF f == "join";
    info.value = info.value.join(', ');
    ELSE;
    TRY; info.value = FILTER $f; info.value; END; CATCH; "Error applying filter to ${info.name}!"; END;
    END;
    END;

    DEFAULT info.id = "form_" _ info.name.replace('\.', '_');
    %]
    <div class="control-group[% IF info.error %] error[% ELSIF info.warning %] warning[% ELSIF info.success %] success[% END %]">
    [% label_field(info) %]
    <div class="[% info.type || 'text' %] controls">
    [%~ IF info.prepend || info.append ~%]<div class="[% IF info.prepend %]input-prepend[% END; IF info.append %] input-append[% END %]">
    [%~ IF info.prepend ~%]<span class="add-on">[% info.prepend %]</span>[%~ END ~%]
    [%~ END ~%]
    <input type="[% info.type || 'text' %]" id="[% info.id %]" name="[% info.name %]"
    [% IF info.pattern %] pattern="[% info.pattern %]" [% END %]
    value="[% info.value | html %]"
    [%~ IF info.classes %] class="[% info.classes.join(" ") %]"[% END ~%]
    [% # The unicode char below is purely to make the placeholder junk work. %]
    [%~ IF info.hint %] placeholder="&#x25E6; [% c.loc(info.hint) %]"[% END ~%]
    [%~ IF info.autocomplete == 0 %] autocomplete="false"[% END ~%]
    [%~ IF info.disabled == 1 %] disabled="disabled"[% END ~%]
    [%~ IF info.tabindex %] tabindex="[% info.tabindex %]"[% END ~%]
    [%~ IF info.maxlength %] maxlength="[% info.maxlength %]"[% END ~%]>
    [%~ IF info.append ~%]<span class="add-on">[% info.append %]</span>[%~ END ~%]
    [%~ IF info.prepend || info.append ~%]</div>[%~ END ~%]
    [% IF info.required || info.error || info.success || info.warning %]
    <span class="help-inline">[% IF info.required %]<span class="req">[% c.loc('FIELD REQUIRED SPLAT') %]</span>[% END; info.error; info.warning; info.success %]</span>
    [% END %]

    [% IF info.support %]<p class="help-block">[% c.loc(info.support) %]</p>[% END %]
    </div>
    </div>
    [% END;

    MACRO textarea_field(info) BLOCK;
    IF !info.defined('value');
    IF results.${context.scope};
    value_name = info.name;
    IF context.scope; value_name = value_name.replace("^${context.scope}.", ""); END;
    info.value = results.${context.scope}.get_original_value(value_name);
    END;
    IF !info.defined('value') && info.default_value;
    info.value = info.default_value;
    END;
    END;

    IF !info.error && messages.for_scope(context.scope);
    value_name = info.name;
    IF context.scope; value_name = value_name.replace("^${context.scope}.", ""); END;
    message = messages.for_scope(context.scope).for_subject(value_name).first_message;
    IF message; info.${message.level} = c.loc(message.id); END;
    END;

    %]
    <div class="control-group[% IF info.error %] error[% ELSIF info.warning %] warning[% ELSIF info.success %] success[% END %]">
    [% label_field(info) %]
    <div class="[% info.type || 'textarea' %] controls">
    <textarea [%= ~%]
    id="form_[% info.name %]" name="[% info.name %]"
    [% IF info.hint %] placeholder="[% c.loc(info.hint) %]"[% END %]
    [%~ IF info.classes %] class="[% info.classes.join(' ') %]"[% END ~%]
    [%~ IF info.disabled == 1 %] disabled="disabled"[% END ~%]
    [%~ IF info.autocomplete == 0 %] autocomplete="false"[% END ~%]
    [%~ IF info.tabindex %] tabindex="[% info.tabindex %]"[% END ~%]
    [%~ IF info.rows %] rows="[% info.rows %]"[% END ~%]
    [%~ IF info.cols %] cols="[% info.cols %]"[% END ~%]
    >
    [%~ info.value | html ~%]
    </textarea>
    [% IF info.error || info.success || info.warning %]
    <span class="help-block">[% IF info.required %]<span class="req">[% c.loc('FIELD REQUIRED SPLAT') %]</span>[% END; info.error; info.warning; info.success %]</span>
    [% END %]
    [% IF info.support %]<p class="help-block">[% c.loc(info.support) %]</p>[% END %]
    </div>
    </div>
    [% END;

    MACRO checkboxes(info) BLOCK;
    IF !info.defined('value') && !info.defined('selected');
    temp = [];
    IF results.${context.scope};
    value_name = info.name;
    IF context.scope; value_name = value_name.replace("^${context.scope}.", ""); END;
    temp = results.${context.scope}.get_original_value(value_name);
    END;

    IF temp.size == 0 && ( info.defined('default_value') || info.defined('default_selected') );
    temp = info.default_selected || info.default_value;
    END;

    info.selected = {};
    FOREACH v IN temp;
    info.selected.$v = v;
    END;
    END;

    IF !info.error && messages.for_scope(context.scope);
    value_name = info.name;
    IF context.scope; value_name = value_name.replace("^${context.scope}.", ""); END;
    IF message; info.${message.level} = c.loc(message.id); END;
    END;
    %]
    <div class="control-group[% IF info.error %] error[% ELSIF info.warning %] warning[% ELSIF info.success %] success[% END %]">
    [% label_field(info) %]
    <div class="checkboxes controls">
    [% FOREACH item = info.options;
    IF info.defined('value_method');
    SET this_value = item.${info.value_method};
    ELSE;
    SET this_value = item;
    END;
    IF info.defined('value_method');
    SET this_name = item.${info.label_method};
    ELSE;
    SET this_name = item;
    END;
    %]<label class="[% IF info.type == 'radio'; "radio"; ELSE; "checkbox"; END %][% IF info.inline %] inline[% END %]"><input type="[% info.type || 'checkbox' %]" name="[% info.name %]" value="[% this_value | html %]"[% IF info.value == this_value || info.selected.defined(this_value) %] checked="checked"[% END %]> [% this_name %]</label>
    [% END %]
    [% IF info.required || info.error || info.success || info.warning %]
    <p class="help-block">
    [% IF info.required %]<span class="req">[% c.loc('FIELD REQUIRED SPLAT') %]</span>[% END %]
    [% info.error; info.warning; info.success %]</span>
    </p>
    [% END %]
    </div>
    </div>
    [% END ~%]

    [%~ MACRO checkbox(info) BLOCK;
    IF !info.defined('checked') && results.${context.scope};
    value_name = info.name;
    IF context.scope; value_name = value_name.replace("${context.scope}.", ""); END;
    info.checked = results.${context.scope}.get_original_value(value_name);
    END;

    IF !info.defined('checked');
    IF results.${context.scope};
    value_name = info.name;
    IF context.scope; value_name = value_name.replace("^${context.scope}.", ""); END;
    info.checked = results.${context.scope}.get_original_value(value_name);
    END;
    IF !info.defined('checked') && info.default_checked;
    info.checked = info.default_checked;
    END;
    END;

    IF !info.error && messages.for_scope(context.scope);
    value_name = info.name;
    IF context.scope; value_name = value_name.replace("^${context.scope}.", ""); END;
    IF message; info.${message.level} = c.loc(message.id); END;
    END;
    %]
    <div class="control-group[% IF info.error %] error[% ELSIF info.warning %] warning[% ELSIF info.success %] success[% END %]">
    [% label_field(info) %]
    <div class="[% info.type || 'text' %] controls">
    <input type="[% info.type || 'checkbox' %]" id="[% info.id %]" name="[% info.name %]"
    [% IF info.pattern %] pattern="[% info.pattern %]" [% END %]
    value="[% info.value | html %]"
    [%~ IF info.classes %] class="[% info.classes.join(" ") %]"[% END ~%]
    [%~ IF info.disabled == 1 %] disabled="disabled"[% END ~%]
    [%~ IF info.checked == 1 %] checked="checked"[% END ~%]
    [%~ IF info.tabindex %] tabindex="[% info.tabindex %]"[% END ~%]
    [%~ IF info.maxlength %] maxlength="[% info.maxlength %]"[% END ~%]>
    [% IF info.required || info.error || info.success || info.warning %]
    <span class="help-inline">[% IF info.required %]<span class="req">[% c.loc('FIELD REQUIRED SPLAT') %]</span>[% END; info.error; info.warning; info.success %]</span>
    [% END %]
    [% IF info.support %]<p class="help-block">[% c.loc(info.support) %]</p>[% END %]
    </div>
    </div>
    [% END ~%]