Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Added tabs element from git://github.com/kaos/nitrogen.git

  • Loading branch information...
commit 6347ae79dd599f41cec5f50d31adeb8057cf9c70 1 parent 6f1f174
@kaos kaos authored
View
26 element_tabs/README
@@ -0,0 +1,26 @@
+This code is written by Andreas Stenius <git@astekk.se>.
+The easiest way to get this into your nitrogen code base is probably to pull it from
+git://github.com/kaos/nitrogen.git
+http://github.com/kaos/nitrogen/tree/master
+
+I'm no git expert (yet), so I'll leave the details of how to pull this as an excercise for the reader.
+
+Please examine the diff.patch file for info about which file goes were in the nitrogen directory
+hierarchy.
+
+You should be able to apply all changes "out-of-the-box" with
+(as hinted by: http://www-cs-students.stanford.edu/~blynn/gitmagic/ch06.html#_patches_the_global_currency )
+
+$ git apply < diff.patch
+
+(I've not tried this myself, please don't blame me if it screws up your working repo).
+
+I ran:
+
+$ git diff 924ce8ab2ed24221f78f77b52c97cb3a35980b7b..HEAD > diff.patch
+
+to generate the file.
+
+Any trouble with this, feel free to give me a shout (at github, user kaos, or by e-mail: git@astekk.se).
+
+--Andreas
View
60 element_tabs/action_tabs_methods.erl
@@ -0,0 +1,60 @@
+% Nitrogen Web Framework for Erlang
+% Copyright (c) 2009 Andreas Stenius
+% See MIT-LICENSE for licensing information.
+
+-module(action_tabs_methods).
+-include("wf.inc").
+-compile(export_all).
+
+render_action(_TriggerPath, TargetPath, Record) ->
+ Script = case element(1, Record) of
+ tab_destroy -> "'destroy'";
+ tab_disable ->
+ "'disable'" ++
+ case Record#tab_disable.tab of
+ -1 -> "";
+ Idx -> wf:f(", ~w", [Idx])
+ end;
+ tab_enable ->
+ "'enable'" ++
+ case Record#tab_enable.tab of
+ -1 -> "";
+ Idx -> wf:f(", ~w", [Idx])
+ end;
+ tab_option ->
+ wf:f("'option', '~s', ~s",
+ [Record#tab_option.key,
+ value_to_js(Record#tab_option.value)]);
+ tab_add ->
+ wf:f("'add', '~s', '~s'",
+ [Record#tab_add.url, Record#tab_add.label])
+ ++
+ case Record#tab_add.index of
+ undefined -> "";
+ Idx -> wf:f(", ~w", [Idx])
+ end;
+ tab_remove ->
+ wf:f("'remove', ~w", [Record#tab_remove.tab]);
+ tab_select ->
+ wf:f("'select', ~w", [Record#tab_select.tab]);
+ tab_load -> wf:f("'load', ~w", [Record#tab_load.tab]);
+ tab_url ->
+ wf:f("'url', ~w, '~s'",
+ [Record#tab_url.tab, Record#tab_url.url]);
+ tab_abort -> "'abort'";
+ tab_rotate ->
+ wf:f("'rotate', ~w, ~s",
+ [Record#tab_rotate.ms, Record#tab_rotate.continuing])
+ end,
+ [wf:me_var(),
+ wf:f("jQuery(obj('~s')).tabs(~s);",
+ [wf:to_js_id(TargetPath), Script])].
+
+value_to_js(Value) when is_list(Value) ->
+ wf:f("'~s'", [wf_utils:js_escape(Value)]);
+value_to_js(Value) when Value == true; Value == false ->
+ wf:f("~s", [Value]);
+value_to_js(Value) when is_atom(Value) ->
+ wf:f("'~s'", [Value]);
+value_to_js(Value) -> wf:f("~p", [Value]).
+
View
550 element_tabs/diff.patch
@@ -0,0 +1,550 @@
+diff --git a/.gitignore b/.gitignore
+index 0d94ff3..0067a2e 100644
+--- a/.gitignore
++++ b/.gitignore
+@@ -1,4 +1,6 @@
+ *.dump
+ .DS_Store
+ http_servers
+-coverage
+\ No newline at end of file
++coverage
++*~
++\#*\#
+diff --git a/Quickstart/reference/actions/tab.txt b/Quickstart/reference/actions/tab.txt
+new file mode 100644
+index 0000000..a6d2915
+--- /dev/null
++++ b/Quickstart/reference/actions/tab.txt
+@@ -0,0 +1,53 @@
++[
++
++{title, "Tab Actions"},
++
++{see_also, [base]},
++
++{usage, "
++ wf:wire(myTabs, #tab_destroy{})
++ <p>
++ wf:wire(myTabs, #tab_disable{ tab=0 })
++ <p>
++ wf:wire(myTabs, #tab_enable{ tab=0 })
++ <p>
++ wf:wire(myTabs, #tab_select{ tab=3 })
++ <p>
++ wf:wire(myTabs, #tab_option{ key=collapsible, value=true })
++ <p>
++ wf:wire(myTabs, #tab_add{ url=\"?q=tab\", label=\"My New Tab\", index=0 })
++ <p>
++ wf:wire(myTabs, #tab_remove{ tab=2 })
++ <p>
++ wf:wire(myTabs, #tab_rotate{ ms=2000, continuing=true })
++ <p>
++ wf:wire(myTabs, #tab_abort{}) % abort tab rotate
++ <p>
++ wf:wire(myTabs, #tab_load{ tab=4 })
++ <p>
++ wf:wire(myTabs, #tab_url{ tab=1, url=\"new-tab-url\" })
++
++"},
++
++
++{description, "
++ These actions operate on a tabs pane (#tabs{}) or one of its tabs.
++ <p>
++ Notice: Not all attributes apply to all tab actions. See usage above.
++
++"},
++
++{attributes, [
++ {"tab", "integer", "Tab index for actions that operate on a specific tab."},
++ {"url", "string", "A relative or absolute local url. No cross domain."},
++ {"label", "string", "New tab caption."},
++ {"index", "integer", "Optional attribute for #tab_add. Insertion index for new tab. Default add to end of tab list."},
++ {"key", "atom", "A valid option key. See the tabs element documentation."},
++ {"value", "bool, integer, string", "Option value for key."},
++ {"ms", "integer", "Number of milliseconds to stay on each tab while rotating."},
++ {"continuing", "bool", "Optional attribute for #tab_rotate. Default false. If set to true, rotate will continue even after the user has selected a tab."}
++]},
++
++{events, []}
++
++].
+diff --git a/Quickstart/reference/elements/link.txt b/Quickstart/reference/elements/link.txt
+index c9e74c8..c30d6af 100644
+--- a/Quickstart/reference/elements/link.txt
++++ b/Quickstart/reference/elements/link.txt
+@@ -21,6 +21,8 @@
+ {attributes, [
+
+ {"text", "string", "The text to display."},
++
++ {"title", "string", "The title for this link. Not rendered on screen, but may be used for other purposes."},
+
+ {"body", "Nitrogen elements", "Instead of text, specify one or more Nitrogen elements (such as an #image) to wrap in a link."},
+
+@@ -32,4 +34,4 @@
+
+ ]}
+
+-].
+\ No newline at end of file
++].
+diff --git a/Quickstart/reference/elements/tabs.txt b/Quickstart/reference/elements/tabs.txt
+new file mode 100644
+index 0000000..37278f8
+--- /dev/null
++++ b/Quickstart/reference/elements/tabs.txt
+@@ -0,0 +1,47 @@
++[
++
++{title, "Tabs Element"},
++
++{see_also, [base]},
++
++{usage, "
++#tabs{
++ tag=tabsTag,
++ options=[
++ {collapsible, true},
++ {selected, 2},
++ {event, mouseover}
++ ],
++ tabs=[
++ #tab{ tag=tabTag, title=\"Tab 1\", body=[\"Tab one body...\"] },
++ #tab{ title=\"Tab 2\", body=#panel{ body=[\"Tab two body...\"] }},
++ #tab{ title=#span{ text=\"<Tab 3>\" },
++ url=\"some/path/to-tab-data\",
++ body=\"This body will be replaced by data from ajax call to above url.\" }
++ ]}
++"},
++
++{description, "The tabs element produces an jQuery UI tabs pane."},
++
++{attributes, [
++
++ {"tag", "Erlang term", "Tag used in event postbacks. Note: no events will be sent if tag is omitted."},
++ {"options", "list", "To Be Written. See jQuery UI docs: http://jqueryui.com/demos/tabs/"},
++ {"tabs", "list of tab elements", "Each tab should have a title and a body. If url is present, then the body is loaded from that url with ajax."}
++]},
++
++{events, [
++
++ {"tabs_event(Type, TabsTag, TabTag, TabIndex)", "
++ Called when a tab is selected, loaded, enabled, disabled, added, removed or shown.<br>
++ The Event provided is a tuple with event kind and the tag of the tabs pane.
++
++ <p>
++ Type is one of: tabsselect, tabsload, tabsshow, tabsadd, tabsremove, tabsenable, tabsdisable.<br>
++ Tag is the attribute provided for the tabs and tab elements respectively.
++ The event will not be triggered if the tabs tag is undefined.
++
++ "}
++]}
++
++].
+diff --git a/Quickstart/src/reference/web_reference_actions.erl b/Quickstart/src/reference/web_reference_actions.erl
+index 05b526c..6c6ca6b 100644
+--- a/Quickstart/src/reference/web_reference_actions.erl
++++ b/Quickstart/src/reference/web_reference_actions.erl
+@@ -59,7 +59,8 @@ column2() ->
+ { "/web/reference/actions/base", "(Base Action)" },
+ { "/web/reference/actions/event", "Event" },
+ { "/web/reference/actions/script", "Script" },
+- { "/web/reference/actions/validate", "Validate" }
++ { "/web/reference/actions/validate", "Validate" },
++ { "/web/reference/actions/tab", "Tabs" }
+ ],
+
+ [
+@@ -129,4 +130,4 @@ read_file(Directory, File, Ext) ->
+ trim(Value) -> lists:reverse(trim1(lists:reverse(trim1(Value)))).
+ trim1([]) -> [];
+ trim1([H|T]) when H==$\s orelse H==$\t orelse H==$\n -> trim1(T);
+-trim1([H|T]) -> [H|T].
+\ No newline at end of file
++trim1([H|T]) -> [H|T].
+diff --git a/Quickstart/src/reference/web_reference_elements.erl b/Quickstart/src/reference/web_reference_elements.erl
+index 028b83a..a683af0 100644
+--- a/Quickstart/src/reference/web_reference_elements.erl
++++ b/Quickstart/src/reference/web_reference_elements.erl
+@@ -38,7 +38,8 @@ column1() ->
+ { "/web/reference/elements/rounded_panel", "Rounded Panel" },
+ { "/web/reference/elements/span", "Span" },
+ { "/web/reference/elements/lightbox", "Lightbox" },
+- { "/web/reference/elements/wizard", "Wizard" }
++ { "/web/reference/elements/wizard", "Wizard" },
++ { "/web/reference/elements/tabs", "Tabs Pane" }
+ ],
+
+ Tables = [
+@@ -60,7 +61,7 @@ column1() ->
+ { "/web/reference/elements/droppable", "Droppable" },
+ { "/web/reference/elements/sortblock", "Sort Block" },
+ { "/web/reference/elements/sortitem", "Sort Item" },
+- { "/web/reference/elements/gravatar", "Gravatar"}
++ { "/web/reference/elements/gravatar", "Gravatar"}
+ ],
+
+ [
+@@ -169,4 +170,4 @@ read_file(Directory, File, Ext) ->
+ trim(Value) -> lists:reverse(trim1(lists:reverse(trim1(Value)))).
+ trim1([]) -> [];
+ trim1([H|T]) when H==$\s orelse H==$\t orelse H==$\n -> trim1(T);
+-trim1([H|T]) -> [H|T].
+\ No newline at end of file
++trim1([H|T]) -> [H|T].
+diff --git a/Quickstart/src/samples/web_samples.erl b/Quickstart/src/samples/web_samples.erl
+index fed46b2..ffea4b0 100644
+--- a/Quickstart/src/samples/web_samples.erl
++++ b/Quickstart/src/samples/web_samples.erl
+@@ -26,7 +26,8 @@ column1() -> [
+ #link { text="Effects", url="/web/samples/effects" }, #br{},
+ #link { text="Drag and Drop", url="/web/samples/dragdrop" }, #br{},
+ #link { text="Sorting", url="/web/samples/sorting1" }, #br{},
+- #link { text="Nested Sorting", url="/web/samples/sorting2" }, #br{}
++ #link { text="Nested Sorting", url="/web/samples/sorting2" }, #br{},
++ #link { text="Tabs Pane", url="/web/samples/tabs" }, #br{}
+ ].
+
+ column2() -> [
+@@ -57,4 +58,4 @@ event(go) ->
+ wf:flash("Hello there"),
+ wf:update(test, "This is a test.");
+
+-event(_) -> ok.
+\ No newline at end of file
++event(_) -> ok.
+diff --git a/Quickstart/src/samples/web_samples_tabs.erl b/Quickstart/src/samples/web_samples_tabs.erl
+new file mode 100644
+index 0000000..7417eaf
+--- /dev/null
++++ b/Quickstart/src/samples/web_samples_tabs.erl
+@@ -0,0 +1,28 @@
++-module (web_samples_tabs).
++-include ("wf.inc").
++-compile(export_all).
++
++main() -> #template { file="./wwwroot/onecolumn.html", bindings=[
++ {'Group', learn},
++ {'Item', samples}
++]}.
++
++title() -> "Tabs Pane Example".
++headline() -> "Tabs Pane Example".
++right() -> linecount:render().
++
++body() -> [
++ #tabs{ tag=tabsTag,
++ options=[ {collapsible, true} ],
++ tabs=[
++ #tab{ tag=tabTag, title="Tab 1", body=["Tab one body..."] },
++ #tab{ title="Tab 2", body=#panel{ body=["Tab two body..."] }},
++ #tab{ title=[" ", #span{ text="<Tab 3>" }, " "],
++ body="Tab three body..." }
++ ]}
++].
++
++event(_) -> ok.
++
++tabs_event(_Evt, _TabsTag, _TabTag, _Index) ->
++ ok.
+diff --git a/Quickstart/wwwroot/css/style.css b/Quickstart/wwwroot/css/style.css
+index 6b77df5..5b7a13c 100644
+--- a/Quickstart/wwwroot/css/style.css
++++ b/Quickstart/wwwroot/css/style.css
+@@ -110,6 +110,9 @@ a:hover { color: #E35; text-decoration: underline; }
+ .content .column1 { width: 47%; margin-right: 3%; float: left; }
+ .content .column2 { width: 47%; float: left; }
+
++.tabs ul li { list-style-type: none; }
++
++
+ /*** FOOTER ***/
+
+ .footer {
+diff --git a/include/wf.inc b/include/wf.inc
+index a6a7157..e147740 100644
+--- a/include/wf.inc
++++ b/include/wf.inc
+@@ -34,7 +34,7 @@
+ -record(p, {?ELEMENT_BASE(element_p), body=""}).
+ -record(label, {?ELEMENT_BASE(element_label), text="", html_encode=true}).
+ -record(value, {?ELEMENT_BASE(element_value), text="", html_encode=true}).
+--record(link, {?ELEMENT_BASE(element_link), text="", body="", html_encode=true, url="javascript:", postback}).
++-record(link, {?ELEMENT_BASE(element_link), text="", title="", body="", html_encode=true, url="javascript:", postback}).
+ -record(error, {?ELEMENT_BASE(element_error), text="", html_encode=true}).
+ -record(span, {?ELEMENT_BASE(element_span), text="", html_encode=true}).
+ -record(button, {?ELEMENT_BASE(element_button), text="Button", html_encode=true, postback}).
+@@ -68,6 +68,9 @@
+ -record(draggable, {?ELEMENT_BASE(element_draggable), tag, body=[], group, handle, clone=true, revert=true}).
+ -record(droppable, {?ELEMENT_BASE(element_droppable), tag, body=[], accept_groups=all, active_class=active, hover_class=hover}).
+ -record(gravatar, {?ELEMENT_BASE(element_gravatar), email="", size="80", rating="g", default=""}).
++-record(tabs, {?ELEMENT_BASE(element_tabs), tabs=[], options=[], tag}).
++-record(tab, {id=wf:temp_id(), title="No Title", body=[], tag, url}).
++
+
+ %%% Controls %%%
+ -define(CONTROL_BASE(Module), ?ELEMENT_BASE(Module), tag).
+@@ -97,6 +100,18 @@
+ -record(animate, {?ACTION_BASE(action_animate), options=[], speed=500, easing=swing}).
+ -record(buttonize, {?ACTION_BASE(action_buttonize)}).
+ -record(comet_start, {?ACTION_BASE(action_comet_start)}).
++-record(tab_destroy, {?ACTION_BASE(action_tabs_methods)}).
++-record(tab_disable, {?ACTION_BASE(action_tabs_methods), tab=-1}).
++-record(tab_enable, {?ACTION_BASE(action_tabs_methods), tab=-1}).
++-record(tab_option, {?ACTION_BASE(action_tabs_methods), key, value}).
++-record(tab_add, {?ACTION_BASE(action_tabs_methods), url, label, index}).
++-record(tab_remove, {?ACTION_BASE(action_tabs_methods), tab}).
++-record(tab_select, {?ACTION_BASE(action_tabs_methods), tab}).
++-record(tab_load, {?ACTION_BASE(action_tabs_methods), tab}).
++-record(tab_url, {?ACTION_BASE(action_tabs_methods), tab, url}).
++%-record(tab_length, {?ACTION_BASE(action_tabs_methods)}).
++-record(tab_abort, {?ACTION_BASE(action_tabs_methods)}).
++-record(tab_rotate, {?ACTION_BASE(action_tabs_methods), ms, continuing=false}).
+
+ %%% Validators %%%
+ -define(VALIDATOR_BASE(Module), module=Module, text="Failed.").
+diff --git a/src/actions/action_tabs_methods.erl b/src/actions/action_tabs_methods.erl
+new file mode 100644
+index 0000000..15b8b44
+--- /dev/null
++++ b/src/actions/action_tabs_methods.erl
+@@ -0,0 +1,60 @@
++% Nitrogen Web Framework for Erlang
++% Copyright (c) 2009 Andreas Stenius
++% See MIT-LICENSE for licensing information.
++
++-module(action_tabs_methods).
++-include("wf.inc").
++-compile(export_all).
++
++render_action(_TriggerPath, TargetPath, Record) ->
++ Script = case element(1, Record) of
++ tab_destroy -> "'destroy'";
++ tab_disable ->
++ "'disable'" ++
++ case Record#tab_disable.tab of
++ -1 -> "";
++ Idx -> wf:f(", ~w", [Idx])
++ end;
++ tab_enable ->
++ "'enable'" ++
++ case Record#tab_enable.tab of
++ -1 -> "";
++ Idx -> wf:f(", ~w", [Idx])
++ end;
++ tab_option ->
++ wf:f("'option', '~s', ~s",
++ [Record#tab_option.key,
++ value_to_js(Record#tab_option.value)]);
++ tab_add ->
++ wf:f("'add', '~s', '~s'",
++ [Record#tab_add.url, Record#tab_add.label])
++ ++
++ case Record#tab_add.index of
++ undefined -> "";
++ Idx -> wf:f(", ~w", [Idx])
++ end;
++ tab_remove ->
++ wf:f("'remove', ~w", [Record#tab_remove.tab]);
++ tab_select ->
++ wf:f("'select', ~w", [Record#tab_select.tab]);
++ tab_load -> wf:f("'load', ~w", [Record#tab_load.tab]);
++ tab_url ->
++ wf:f("'url', ~w, '~s'",
++ [Record#tab_url.tab, Record#tab_url.url]);
++ tab_abort -> "'abort'";
++ tab_rotate ->
++ wf:f("'rotate', ~w, ~s",
++ [Record#tab_rotate.ms, Record#tab_rotate.continuing])
++ end,
++ [wf:me_var(),
++ wf:f("jQuery(obj('~s')).tabs(~s);",
++ [wf:to_js_id(TargetPath), Script])].
++
++value_to_js(Value) when is_list(Value) ->
++ wf:f("'~s'", [wf_utils:js_escape(Value)]);
++value_to_js(Value) when Value == true; Value == false ->
++ wf:f("~s", [Value]);
++value_to_js(Value) when is_atom(Value) ->
++ wf:f("'~s'", [Value]);
++value_to_js(Value) -> wf:f("~p", [Value]).
++
+diff --git a/src/elements/html/element_link.erl b/src/elements/html/element_link.erl
+index d8ddbd8..b5ed627 100644
+--- a/src/elements/html/element_link.erl
++++ b/src/elements/html/element_link.erl
+@@ -23,5 +23,6 @@ render(ControlID, Record) ->
+ {id, ControlID},
+ {href, Record#link.url},
+ {class, [link, Record#link.class]},
+- {style, Record#link.style}
+- ]).
+\ No newline at end of file
++ {style, Record#link.style},
++ {title, wf:html_encode(Record#link.title, Record#link.html_encode)}
++ ]).
+diff --git a/src/elements/layout/element_tabs.erl b/src/elements/layout/element_tabs.erl
+index c039ea3..2554eed 100644
+--- a/src/elements/layout/element_tabs.erl
++++ b/src/elements/layout/element_tabs.erl
+@@ -9,30 +9,57 @@
+ reflect() -> record_info(fields, tabs).
+
+ render(ControlID, Record) ->
++ PickledPostBackInfo = case Record#tabs.tag of
++ undefined -> "false";
++ Tag ->
++ [wf:wire(wf:f(
++ "Nitrogen.$tab(obj('~s'), '~s')",
++ [R#tab.id, wf_utils:pickle(R#tab.tag)]))
++ || R <- [#tab{ id=ControlID, tag=Record#tabs.tag }
++ |Record#tabs.tabs], R#tab.tag /= undefined],
++
++ wf:f("'~s'",
++ [action_event:make_postback_info(
++ Tag, tabsevent,
++ ControlID, ControlID,
++ ?MODULE)
++ ])
++ end,
++
+ Options = action_jquery_effect:options_to_js(
+ Record#tabs.options),
+- Script = wf:f("Nitrogen.$tabs(obj('~s'), ~s);",
+- [ControlID, Options]),
++
++ Script = wf:f("Nitrogen.$tabs(obj('~s'), ~s, ~s)",
++ [ControlID, Options, PickledPostBackInfo]),
+
+ wf:wire(Script),
+-
++
+ Terms = #panel{
+ class = "tabs " ++ wf:to_list(Record#tabs.class),
+ style = Record#tabs.style,
+- body = ["<ul>"]
+- ++ [[wf:f("<li><a href='#~s'>", [html_id(Tab#tab.id)]),
+- Tab#tab.title,
+- "</a></li>"]
+- || Tab <- Record#tabs.tabs]
+- ++ ["</ul>"]
+- ++ [#panel{ id = Tab#tab.id,
+- class = "tab",
+- body = Tab#tab.body }
+- || Tab <- Record#tabs.tabs]
++ body = [
++ #list{ body=[
++ #listitem{ body=tab_link(Tab) }
++ || Tab <- Record#tabs.tabs ] },
++ [#panel{ id = Tab#tab.id,
++ class = "tab",
++ body = Tab#tab.body }
++ || Tab <- Record#tabs.tabs]
++ ]
+ },
+
+ element_panel:render(ControlID, Terms).
+
++event(TabsTag) ->
++ [EventStr] = wf:q(event),
++ [IndexStr] = wf:q(tab_index),
++ [TabItem] = wf:q(tab_tag),
++ Event = list_to_atom(EventStr),
++ Index = list_to_integer(IndexStr),
++ TabTag = wf:depickle(TabItem),
++ Module = wf_platform:get_page_module(),
++ Module:tabs_event(Event, TabsTag, TabTag, Index).
++
+ html_id(Id) ->
+ case wf_path:is_temp_element(Id) of
+ true ->
+@@ -43,3 +70,8 @@ html_id(Id) ->
+ wf_path:pop_path(),
+ HtmlId
+ end.
++
++tab_link(#tab{ url=undefined, id=Id, title=Title }) ->
++ #link{ url="#" ++ html_id(Id), body=Title };
++tab_link(#tab{ url=Url, id=Id, title=Title }) ->
++ #link{ url=Url, title=html_id(Id), body=Title }.
+diff --git a/test/src/elements/html/element_link_test.erl b/test/src/elements/html/element_link_test.erl
+index fc07f5c..641296b 100644
+--- a/test/src/elements/html/element_link_test.erl
++++ b/test/src/elements/html/element_link_test.erl
+@@ -34,6 +34,6 @@ basic_test_() ->
+ ?_assert(eunit_helper:regexpMatch("<a id='4' href='javascript:' class='link'><img id='.*?' class='image' src='/path/to/image.gif'/></a>",
+ new_link_4())),
+ ?_assertEqual("<a id='5' href='not_javascript' class='link'>LINK&nbsp;TEXTA LINK BODY</a>", new_link_5()),
+- ?_assertEqual([module,id,actions,show_if,class,style,text,body, html_encode,url,postback],
++ ?_assertEqual([module,id,actions,show_if,class,style,text,title,body,html_encode,url,postback],
+ element_link:reflect())
+ ].
+diff --git a/www/elements.css b/www/elements.css
+index eead389..7106aeb 100644
+--- a/www/elements.css
++++ b/www/elements.css
+@@ -140,4 +140,12 @@ input.LV_invalid_field:active,
+ textarea.LV_invalid_field:hover,
+ textarea.LV_invalid_field:active {
+ border: 1px solid #CC0000;
+-}*/
+\ No newline at end of file
++}*/
++
++
++/*** TABS ***/
++
++.ui-tabs .ui-tabs-hide {
++ display: none;
++}
++
+diff --git a/www/nitrogen.js b/www/nitrogen.js
+index 6c0885a..53c59bc 100644
+--- a/www/nitrogen.js
++++ b/www/nitrogen.js
+@@ -456,5 +456,43 @@ N.$sortblock = function(sortBlock, sortOptions, sortPostbackInfo) {
+ }
+
+
++/*** TABS ***/
++
++N.$tab = function(tabObj, tabTag) {
++ tabObj.$tab_tag = tabTag;
++}
++
++N.$tabs = function(tabsObj, tabsOptions, tabsPostbackInfo) {
++ if (tabsPostbackInfo)
++ {
++ var n = Nitrogen.$lookup(Nitrogen.$current_id);
++ var evt_fun = function(ev, ui) {
++ var tag;
++ if (ui.index >= 0) {
++ tag = ui.panel.$tab_tag;
++ } else {
++ tag = tabsObj.$tab_tag;
++ }
++
++ n.$queue_event(this.id, tabsPostbackInfo,
++ "event=" + ev.type +
++ "&tab_index=" + ui.index +
++ "&tab_tag=" + tag);
++ }
++
++ tabsOptions.select = evt_fun;
++ tabsOptions.load = evt_fun;
++ tabsOptions.show = evt_fun;
++ tabsOptions.add = evt_fun;
++ tabsOptions.remove = evt_fun;
++ tabsOptions.enable = evt_fun;
++ tabsOptions.disable = evt_fun;
++ }
++
++ jQuery(tabsObj).tabs(tabsOptions);
++}
++
++
++
+ Nitrogen.$event_loop();
+ Nitrogen.Page({ id : 'page' });
View
77 element_tabs/element_tabs.erl
@@ -0,0 +1,77 @@
+% Nitrogen Web Framework for Erlang
+% Copyright (c) 2009 Andreas Stenius
+% See MIT-LICENSE for licensing information.
+
+-module (element_tabs).
+-include ("wf.inc").
+-compile(export_all).
+
+reflect() -> record_info(fields, tabs).
+
+render(ControlID, Record) ->
+ PickledPostBackInfo = case Record#tabs.tag of
+ undefined -> "false";
+ Tag ->
+ [wf:wire(wf:f(
+ "Nitrogen.$tab(obj('~s'), '~s')",
+ [R#tab.id, wf_utils:pickle(R#tab.tag)]))
+ || R <- [#tab{ id=ControlID, tag=Record#tabs.tag }
+ |Record#tabs.tabs], R#tab.tag /= undefined],
+
+ wf:f("'~s'",
+ [action_event:make_postback_info(
+ Tag, tabsevent,
+ ControlID, ControlID,
+ ?MODULE)
+ ])
+ end,
+
+ Options = action_jquery_effect:options_to_js(
+ Record#tabs.options),
+
+ Script = wf:f("Nitrogen.$tabs(obj('~s'), ~s, ~s)",
+ [ControlID, Options, PickledPostBackInfo]),
+
+ wf:wire(Script),
+
+ Terms = #panel{
+ class = "tabs " ++ wf:to_list(Record#tabs.class),
+ style = Record#tabs.style,
+ body = [
+ #list{ body=[
+ #listitem{ body=tab_link(Tab) }
+ || Tab <- Record#tabs.tabs ] },
+ [#panel{ id = Tab#tab.id,
+ class = "tab",
+ body = Tab#tab.body }
+ || Tab <- Record#tabs.tabs]
+ ]
+ },
+
+ element_panel:render(ControlID, Terms).
+
+event(TabsTag) ->
+ [EventStr] = wf:q(event),
+ [IndexStr] = wf:q(tab_index),
+ [TabItem] = wf:q(tab_tag),
+ Event = list_to_atom(EventStr),
+ Index = list_to_integer(IndexStr),
+ TabTag = wf:depickle(TabItem),
+ Module = wf_platform:get_page_module(),
+ Module:tabs_event(Event, TabsTag, TabTag, Index).
+
+html_id(Id) ->
+ case wf_path:is_temp_element(Id) of
+ true ->
+ wf_path:to_html_id(Id);
+ false ->
+ wf_path:push_path(Id),
+ HtmlId = wf_path:to_html_id(wf_path:get_path()),
+ wf_path:pop_path(),
+ HtmlId
+ end.
+
+tab_link(#tab{ url=undefined, id=Id, title=Title }) ->
+ #link{ url="#" ++ html_id(Id), body=Title };
+tab_link(#tab{ url=Url, id=Id, title=Title }) ->
+ #link{ url=Url, title=html_id(Id), body=Title }.
View
151 element_tabs/elements.css
@@ -0,0 +1,151 @@
+.label {
+ display: block;
+ font-weight: bold;
+ font-size: 0.9em;
+ margin-top: 7px;
+}
+
+.value {
+ display: block;
+ font-weight: bold;
+ padding: 0px 0px 0px 0px;
+ margin-top: 2px;
+ margin-bottom: 10px;
+ color: #000000;
+}
+
+/*** ROUNDED PANEL ***/
+table.rounded_panel.black td { background: #000000; }
+table.rounded_panel.gray td { background: #F1F1F1; }
+table.rounded_panel.black td { background: #000000; }
+table.rounded_panel.white td { background: #FFFFFF; }
+table.rounded_panel tr.chrome td { font-size: 1px; line-height: 1px; padding: 0 0 0 0; margin: 0 0 0 0; border: 0; }
+table.rounded_panel tr.chrome img { padding: 0 0 0 0; margin: 0 0 0 0; border: 0; }
+
+
+table.rounded_panel { margin: 0px 0px 0px 0px; padding: 0px 0px 0px 0px; border: 0px; }
+table.rounded_panel td { padding: 0px 0px 0px 0px; }
+table.rounded_panel td.content { padding: 4px 20px 4px 20px; }
+
+table.rounded_panel { margin: 0px 0px 0px 0px; padding: 0px 0px 0px 0px; border: 0px; }
+table.rounded_panel td { padding: 0px 0px 0px 0px; }
+table.rounded_panel td.content { padding: 4px 20px 4px 20px; }
+
+
+/*** INPLACE EDITING ***/
+
+div.inplace_textbox div.view {
+ cursor: pointer;
+}
+
+div.inplace_textbox .label {
+ display: inline;
+}
+
+div.inplace_textbox .instructions {
+ padding-left: 12px;
+ font-size: 0.8em;
+}
+
+div.inplace_textbox .LV_invalid {
+ position: relative;
+ margin-right: 7px;
+ color:#CC0000;
+ background-color: #FFFF99;
+ border: solid 2px #D0D0C0;
+}
+
+.flash_container {
+}
+
+.flash {
+ color: #303030;
+ background-color: #FAEC7F;
+ border: #FFE800 solid 2px;
+ padding: 3px 5px 3px 5px;
+ margin-bottom: 7px;
+}
+
+.flash .flash_content {
+ margin-right: 50px;
+}
+
+.flash .flash_close_button {
+ float: right;
+ font-size: 90%;
+}
+
+.wizard {
+}
+
+.wizard .wizard_title {
+ font-family: 'Arial', Georgia; font-size: 1.2em; font-weight: bold;
+}
+
+.wizard .wizard_body {
+ margin-top: 20px;
+ margin-bottom: 20px;
+}
+
+.wizard .wizard_buttons {
+ padding-right: 40px;
+ text-align:right;
+}
+
+.wizard .wizard_buttons input {
+ margin-right: 20px;
+}
+
+/*** VALIDATION ***/
+
+.LV_validation_message {
+ vertical-align: middle;
+ display: inline;
+ line-height: 100%;
+ position: absolute;
+ font-weight:bold;
+ margin: 0px 0px 0px 7px;
+ padding: 5px 7px 5px 7px;
+ opacity: 0.8;
+ -moz-opacity: 0.8;
+ filter:alpha(opacity=80);
+}
+
+/*
+.LV_valid {
+ background-image: url(/nitrogen/checkmark.png);
+ margin: 2px 0px 5px 0px;
+ padding: 0px 20px 20px 0px;
+ color:#00CC00;
+}
+*/
+
+.LV_invalid {
+ color:#CC0000;
+ background-color: #FFFF99;
+ border: solid 2px #D0D0C0;
+}
+
+/*.LV_valid_field,
+input.LV_valid_field:hover,
+input.LV_valid_field:active,
+textarea.LV_valid_field:hover,
+textarea.LV_valid_field:active {
+ border: 1px solid #00CC00;
+}
+
+.LV_invalid_field,
+input.LV_invalid_field:hover,
+input.LV_invalid_field:active,
+textarea.LV_invalid_field:hover,
+textarea.LV_invalid_field:active {
+ border: 1px solid #CC0000;
+}*/
+
+
+/*** TABS ***/
+
+.ui-tabs .ui-tabs-hide {
+ display: none;
+}
+
View
498 element_tabs/nitrogen.js
@@ -0,0 +1,498 @@
+/*
+Usage:
+ var n = new Nitrogen({
+ url : "http://nitrogenserver/web/module",
+ div : enclosingDiv
+ });
+
+ n.IFrame(div, url);
+ n.Inline(div, url);
+ n.Windex(div, url);
+
+*/
+
+function Nitrogen(o) {
+ // Set the id, and associate with the global Nitrogen object...
+ if (o.id) {
+ this.id = o.id
+ } else {
+ this.id = "o" + Math.floor(Math.random()*999999999);
+ }
+ eval(Nitrogen.$NString + "." + this.id + " = this;");
+
+ // Set the originating URL...
+ if (o.url) {
+ this.$url = o.url;
+ } else {
+ this.$url = document.location.href;
+ }
+
+ // Set some initial properties.
+ if (o.div) {
+ this.$div = o.div;
+ } else {
+ this.$div = document;
+ }
+
+ // Clear the dom_state...
+ this.$dom_state = "";
+ this.$comet_is_running = false;
+}
+
+var N = Nitrogen;
+N.$NString = "Nitrogen";
+N.$current_id = "";
+N.$current_path = "";
+N.$event_queue = new Array();
+N.$event_is_running = false;
+
+/*** PUBLIC METHODS ***/
+
+function obj(path) {
+ return Nitrogen.obj(path);
+}
+
+N.Page = function(o) {
+ var n = new Nitrogen(o);
+ n.$do_event = n.$do_xhr_event;
+ n.$do_comet = n.$do_xhr_comet;
+ return n;
+}
+
+N.Inline = function(o) {
+ var n = new Nitrogen(o);
+ if (o.windex) {
+ n.$do_event = n.$do_windex_event;
+ n.$do_comet = n.$do_windex_comet;
+ n.$url = Nitrogen.$add_param(n.$url, "windex", "true");
+ } else {
+ n.$do_event = n.$do_xhr_event;
+ n.$do_comet = n.$do_xhr_comet;
+ }
+
+ var url = Nitrogen.$add_param(n.$url, "object_id", n.id);
+ Nitrogen.$load_script(url);
+ return n;
+}
+
+
+/*** PRIVATE METHODS ***/
+
+N.$lookup = function(id) {
+ return eval(Nitrogen.$NString + "." + id + ";");
+}
+
+N.$set_dom_state = function(s) {
+ var n = Nitrogen.$lookup(Nitrogen.$current_id);
+ n.$set_dom_state(s);
+}
+
+N.prototype.$set_dom_state = function(s) {
+ this.$dom_state = s;
+}
+
+
+
+/*** EVENT QUEUE ***/
+
+N.$queue_event = function(triggerID, postbackInfo, extraParams) {
+ var n = Nitrogen.$lookup(Nitrogen.$current_id);
+ n.$queue_event(triggerID, postbackInfo, extraParams);
+}
+
+N.prototype.$queue_event = function(triggerID, postbackInfo, extraParams) {
+ // Put an event on the event_queue.
+ Nitrogen.$event_queue.push({
+ n : this,
+ triggerID : this.obj(triggerID).id,
+ postbackInfo : postbackInfo,
+ extraParams : extraParams
+ });
+}
+
+
+N.$event_loop = function() {
+ // Make it loop.
+ setTimeout(Nitrogen.$NString + ".$event_loop();", 1);
+
+ // If something is running, or the queue is empty, then just return.
+ if (Nitrogen.$event_is_running) return;
+ if (Nitrogen.$event_queue.length == 0) return;
+
+ // Get and exect the event.
+ var o = Nitrogen.$event_queue.shift();
+ o.n.$do_event(o.triggerID, o.postbackInfo, o.extraParams);
+}
+
+/*** VALIDATE AND SERIALIZE ***/
+
+N.prototype.$validate_and_serialize = function(triggerID) {
+ // Check validatation and build params...
+ var s = "";
+ var is_valid = true;
+ var elements = this.$get_elements_to_serialize();
+ for (var i=0; i<elements.length; i++) {
+ element = elements[i];
+ if (element.validator && (element.validator.trigger.id == triggerID) && !element.validator.validate()) {
+ is_valid = false;
+ } else {
+ if (element.type == "radio") {
+ s += "&" + element.id + "=" + element.checked;
+ }
+
+ s += "&" + jQuery(element).serialize();
+ }
+ }
+
+ // Return the params if valid. Otherwise, return null.
+ if (is_valid) {
+ return s;
+ } else {
+ return null;
+ }
+}
+
+/*** AJAX METHODS ***/
+
+N.prototype.$do_xhr_event = function(triggerID, postbackInfo, extraParams) {
+ // Flag to prevent firing multiple postbacks at the same time...
+ Nitrogen.$event_is_running = true;
+
+ // Run validation...
+ var s = this.$validate_and_serialize(triggerID);
+ if (s == null) {
+ Nitrogen.$event_is_running = false;
+ return;
+ }
+
+ // Build params...
+ var params =
+ "domState=" + this.$dom_state +
+ "&postbackInfo=" + postbackInfo +
+ "&" + s +
+ "&" + extraParams;
+
+ jQuery.ajax({
+ url: this.$url,
+ type:'post',
+ data: params,
+ dataType: 'text',
+ success: function(data, textStatus) {
+ Nitrogen.$event_is_running = false;
+ eval(data);
+ },
+ error: function(xmlHttpRequest, textStatus, errorThrown) {
+ Nitrogen.$event_is_running = false;
+ }
+ });
+}
+
+N.$comet_start = function(postbackInfo) {
+ var n = Nitrogen.$lookup(Nitrogen.$current_id);
+ n.$comet_start(postbackInfo);
+}
+
+N.prototype.$comet_start = function(postbackInfo) {
+ this.$do_comet(postbackInfo);
+}
+
+N.prototype.$do_xhr_comet = function(postbackInfo) {
+ if (this.$comet_is_running) return;
+ this.$comet_is_running = true;
+
+ // Get params...
+ var params =
+ "postbackInfo=" + postbackInfo +
+ "&domState=" + this.$dom_state;
+
+ var n = this;
+
+ $.ajax({
+ url: this.$url,
+ type:'post',
+ data: params,
+ dataType: 'text',
+ success: function(data, textStatus) {
+ eval(data);
+ n.$comet_is_running = false;
+ setTimeout("Nitrogen." + n.id + ".$comet_start('" + postbackInfo + "');", 0);
+ },
+ error: function(xmlHttpRequest, textStatus, errorThrown) {
+ n.$comet_is_running = false;
+ setTimeout("Nitrogen." + n.id + ".$comet_start('" + postbackInfo + "');", 5000);
+ }
+ });
+}
+
+
+
+/*** WINDEX METHODS ***/
+
+N.prototype.$do_windex_event = function(triggerID, postbackInfo, extraParams) {
+ // Run validation...
+ var s = this.$validate_and_serialize(triggerID);
+ if (s == null) {
+ return;
+ }
+
+ // Build params...
+ var url = this.$url;
+ url = Nitrogen.$add_param(url, "domState", this.$dom_state);
+ url = Nitrogen.$add_param(url, "postbackInfo", postbackInfo);
+ url = Nitrogen.$add_param(url, s);
+ url = Nitrogen.$add_param(url, extraParams);
+ Nitrogen.$load_script(url);
+}
+
+
+N.prototype.$do_windex_comet = function(postbackInfo) {
+ alert("Comet is not yet supported via Windex.");
+}
+
+/*** FILE UPLOAD ***/
+N.$upload = function(form) {
+ var n = Nitrogen.$lookup(Nitrogen.$current_id);
+ form.domState.value = n.$dom_state;
+ form.submit();
+ form.reset();
+}
+
+/*** SERIALIZATION ***/
+
+N.prototype.$get_elements_to_serialize = function() {
+ var tagnames = ["input", "button", "select", "textarea", "checkbox"];
+ var a = new Array();
+ for (var i=0; i<tagnames.length; i++) {
+ var l = this.$div.getElementsByTagName(tagnames[i]);
+ for (var j=0; j<l.length; j++) {
+ var elementName = l[j].name;
+ if (elementName != "domState" && elementName != "postbackInfo") {
+ a = a.concat(l[j]);
+ }
+ }
+ }
+
+ return a;
+}
+
+/*** PATH LOOKUPS ***/
+
+N.obj = function(path) {
+ var n = Nitrogen.$lookup(Nitrogen.$current_id);
+ return n.obj(path);
+}
+
+N.prototype.obj = function(path) {
+ path = N.$normalize_partial_path(path);
+
+ // Try the easy option...
+ var el = document.getElementById(path);
+ if (el) return el;
+
+ // Not found, so scan recursively...
+ return Nitrogen.$scan_elements(path, this.$div.childNodes);
+}
+
+N.$normalize_partial_path = function(path) {
+ var oldparts = Nitrogen.$current_path.split(".");
+ var newparts = path.split(".");
+ var a = new Array();
+ for (var i=0; i<newparts.length; i++) {
+ var part = newparts[i];
+ if (part == "me") a = oldparts;
+ else if (part == "parent") a.pop();
+ else a.push(part);
+ }
+
+ return a.join("__");
+}
+
+N.$scan_elements = function(path, elements) {
+ if (!elements) return;
+
+ for (var i=0; i<elements.length; i++) {
+ var t = elements[i].id;
+ if (t == undefined) continue;
+ var pos = t.indexOf(path);
+ if (pos == -1) continue;
+ if (t.indexOf(path) + path.length == t.length) {
+ return elements[i];
+ }
+ }
+
+ for (var i=0; i<elements.length; i++) {
+ var el = Nitrogen.$scan_elements(path, elements[i].childNodes)
+ if (el) return el;
+ }
+
+ return null;
+}
+
+
+/*** EVENT WIRING ***/
+
+N.$observe_event = function(el, type, func) {
+ jQuery(el).bind(type, func);
+}
+
+
+
+/*** DYNAMIC UPDATING ***/
+
+N.$update = function(el, html) {
+ jQuery(el).html(html);
+}
+
+N.prototype.$update = function(html) {
+ jQuery(this.$div).html(html);
+}
+
+N.$insert_top = function(el, html) {
+ jQuery(el).prepend(html);
+}
+
+N.$insert_bottom = function(el, html) {
+ jQuery(el).append(html);
+}
+
+
+/*** MISC ***/
+
+N.$return_false = function(value, args) {
+ return false;
+}
+
+N.$is_enter_key = function(event) {
+ return (event && event.keyCode == 13);
+}
+
+N.$go_next = function(controlID) {
+ var o = Nitrogen.obj(controlID);
+ if (o.focus) o.focus();
+ if (o.select) o.select();
+ if (o.click) o.click();
+}
+
+N.$disable_selection = function(element) {
+ element.onselectstart = function() {
+ return false;
+ };
+ element.unselectable = "on";
+ element.style.MozUserSelect = "none";
+ element.style.cursor = "default";
+}
+
+N.$set_value = function(element, value) {
+ if (!element.id) element = obj(element);
+ if (element.value != undefined) element.value = value;
+ else if (element.checked != undefined) element.checked = value;
+ else this.$update(element, value);
+}
+
+N.$add_param = function(url, key, value) {
+ // Create the key=value line to add.
+ // Sometimes, the user will pass a bunch of params in the key field.
+ var s = "";
+ if (key) { s = key; }
+ if (key && value) { s = key + "=" + value; }
+
+ // Return the updated url...
+ var parts = url.split("?");
+ if (parts.length == 1) { return url + "?" + s; }
+ if (parts.length > 1) { return url + "&" + s; }
+}
+
+N.$load_script = function(url) {
+ var head = document.getElementsByTagName('head')[0];
+ var script = document.createElement('script');
+ script.type= 'text/javascript';
+ script.src= url;
+ head.appendChild(script);
+}
+
+/*** DATE PICKER ***/
+
+N.$datepicker = function(pickerObj, pickerOptions) {
+ jQuery(pickerObj).datepicker(pickerOptions);
+}
+
+
+/*** DRAG AND DROP ***/
+
+N.$draggable = function(dragObj, dragOptions, dragTag) {
+ dragObj.$drag_tag = dragTag;
+ jQuery(dragObj).draggable(dragOptions);
+}
+
+N.$droppable = function(dropObj, dropOptions, dropPostbackInfo) {
+ var n = Nitrogen.$lookup(Nitrogen.$current_id);
+ dropOptions.drop = function(ev, ui) {
+ var dragItem = ui.draggable[0].$drag_tag;
+ n.$queue_event(this.id, dropPostbackInfo, "drag_item=" + dragItem);
+ }
+ jQuery(dropObj).droppable(dropOptions);
+}
+
+
+
+/*** SORTING ***/
+
+N.$sortitem = function(sortItem, sortTag) {
+ sortItem.$sort_tag = sortTag;
+}
+
+N.$sortblock = function(sortBlock, sortOptions, sortPostbackInfo) {
+ var n = Nitrogen.$lookup(Nitrogen.$current_id);
+ sortOptions.update = function() {
+ var sortItems = "";
+ for (var i=0; i<this.childNodes.length; i++) {
+ var childNode = this.childNodes[i];
+ if (sortItems != "") sortItems += ",";
+ if (childNode.$sort_tag) sortItems += childNode.$sort_tag;
+ }
+ n.$queue_event(this.id, sortPostbackInfo, "sort_items=" + sortItems);
+ };
+ jQuery(sortBlock).sortable(sortOptions);
+}
+
+
+/*** TABS ***/
+
+N.$tab = function(tabObj, tabTag) {
+ tabObj.$tab_tag = tabTag;
+}
+
+N.$tabs = function(tabsObj, tabsOptions, tabsPostbackInfo) {
+ if (tabsPostbackInfo)
+ {
+ var n = Nitrogen.$lookup(Nitrogen.$current_id);
+ var evt_fun = function(ev, ui) {
+ var tag;
+ if (ui.index >= 0) {
+ tag = ui.panel.$tab_tag;
+ } else {
+ tag = tabsObj.$tab_tag;
+ }
+
+ n.$queue_event(this.id, tabsPostbackInfo,
+ "event=" + ev.type +
+ "&tab_index=" + ui.index +
+ "&tab_tag=" + tag);
+ }
+
+ tabsOptions.select = evt_fun;
+ tabsOptions.load = evt_fun;
+ tabsOptions.show = evt_fun;
+ tabsOptions.add = evt_fun;
+ tabsOptions.remove = evt_fun;
+ tabsOptions.enable = evt_fun;
+ tabsOptions.disable = evt_fun;
+ }
+
+ jQuery(tabsObj).tabs(tabsOptions);
+}
+
+
+
+Nitrogen.$event_loop();
+Nitrogen.Page({ id : 'page' });
View
139 element_tabs/style.css
@@ -0,0 +1,139 @@
+html { background: #fff; }
+body {
+ background: #fff url(../images/body_bg.png) no-repeat fixed 20px 25px;
+ color: #444;
+ font-size: 0.8em;
+ font-family: "Lucida Grande", "Segoe UI", "Trebuchet MS", Tahoma, Helvetica, Arial, sans-serif;
+ line-height: 1.6em;
+ margin: 0px;
+}
+
+a { color: #E35; text-decoration: none; }
+a:hover { color: #E35; text-decoration: underline; }
+
+/*** CONTENT/LEFT/MIDDLE/RIGHT ***/
+
+.leftcolumn, .rightcolumn { width: 18%; }
+.middlecolumn { width: 50%; }
+.leftcolumn, .middlecolumn, .rightcolumn { float: left; padding: 0% 2%; }
+.leftcolumn { text-align: right; }
+.rightcolumn { text-align: left; border-left: solid 1px #DDD; }
+
+/*** TOP MENU1 ***/
+
+.topmenu1 {
+ padding-top: 60px;
+ background: #222 url(../images/logo_small.png) no-repeat 20px 25px;
+ width: 100%;
+ overflow: hidden;
+}
+
+.topmenu1 ul { float: left; width: 2000px; margin-left: 25%; }
+.topmenu1 ul li { display: block; float: left; text-decoration: none; }
+.topmenu1 ul li a {
+ font-size: 0.9em;
+ display: block;
+ color: #444;
+ padding: 1px 12px 3px 12px;
+ margin: 0px 3px;
+ text-decoration: none;
+ text-transform: uppercase;
+}
+.topmenu1 ul:hover li a { color: #fff; }
+.topmenu1 ul li a:hover { background-color: #333; }
+.topmenu1 ul li a.selected {
+ color: #E35;
+ background-color: #DDD;
+ font-weight: bold;
+}
+
+/*** TOP MENU2 ***/
+
+.topmenu2 {
+ background: #DDD;
+ padding-top: 15px;
+ width: 100%;
+ overflow: hidden;
+ margin-bottom: 30px;
+}
+
+.topmenu2 ul { float: left; width: 2000px; margin-left: 25%; }
+.topmenu2 ul li { display: block; float: left; text-decoration: none; }
+.topmenu2 ul li a {
+ font-size: 0.85em;
+ display: block;
+ color: #AAA;
+ padding: 1px 12px 3px 12px;
+ margin: 0px 3px;
+ text-decoration: none;
+ text-transform: uppercase;
+}
+
+.topmenu2 ul:hover li a { color: #777; }
+.topmenu2 ul li a.selected {
+ color: #E35;
+ background-color: #FFF;
+ font-weight: bold;
+}
+.topmenu2 ul li a:hover { color: #E35; }
+
+/*** LEFT MENU ***/
+
+.leftmenu h1 { font-size: 1.8em; color: #F70; clear: both; letter-spacing: -2px; }
+.leftmenu h2 { font-size: 1.4em; color: #9C9; clear: both; letter-spacing: -2px; }
+.leftmenu h3 { font-size: 1.0em; color: #FFF; letter-spacing: -1px; }
+.leftmenu h4 { font-size: 0.8em; color: #FFF; text-transform: uppercase; }
+.leftmenu h1, .leftmenu h2, .leftmenu h3, .leftmenu h4 { font-weight: normal; margin-top: 1em; margin-bottom: 0.4em; }
+
+.leftmenu ul li { display: block; }
+.leftmenu ul li a { font-size: 0.9em; display: block; color: #CCC; text-decoration: none; padding: 6px 0px; }
+.leftmenu ul:hover li a { color: #999; }
+.leftmenu ul li a.selected { color: #E35; text-decoration: none; font-weight: bold; }
+.leftmenu ul li a:hover { color: #E35; }
+
+
+/*** CONTENT ***/
+
+.content .headline { font-size: 2.2em; color: #DB0; clear: both; letter-spacing: -1px; font-weight: normal; margin-bottom: 0.7em; }
+.content h1 { font-size: 2.2em; color: #BBB; clear: both; letter-spacing: -1px; }
+.content h2 { font-size: 1.5em; color: #DB0; clear: both; letter-spacing: -1px; }
+.content h3 { font-size: 1.2em; color: #79C; letter-spacing: -1px; }
+.content h4 { font-size: 0.9em; color: #777; text-transform: uppercase; }
+.content h1, .content h2, .content h3, .content h4 { font-weight: normal; margin-top: 1em; margin-bottom: 0.5em; }
+.content h1:first-child, h2:first-child, h3:first-child, h4:first-child { margin-top: 0em; }
+.content ul { padding-left: 7px; }
+.content ul li { list-style: disc inside; }
+.content p { margin-top: 10px; }
+
+.content hr { margin-top: 30px; margin-bottom: 30px; color: #ddd; }
+
+.content .column1 { width: 47%; margin-right: 3%; float: left; }
+.content .column2 { width: 47%; float: left; }
+
+.tabs ul li { list-style-type: none; }
+
+
+/*** FOOTER ***/
+
+.footer {
+ color: #272;
+ background: #9C9 url(../images/footer_bg.png);
+ background-color: #9C9;
+ border-top: solid 5px #8B8;
+ border-bottom: solid 5px #8B8;
+ padding: 20px 0px;
+ margin: 40px 0px;
+}
+
+.footer > * { margin-left: 20%; margin-right: 20%; }
+.footer > ul > li { width: 22%; float: left; text-transform: uppercase; padding-right: 2%; }
+.footer > ul > li > ul > li { text-transform: none; margin: 3px 0px; }
+.footer a { color: #494; }
+/*** LEGAL ***/
+
+.legal { color: #fff; background-color: #000; padding: 10px 0px; font-size: 0.85em; }
+.legal > * { margin-left: 25%; margin-right: 25%; }
+.legal a { color: #EE0; text-decoration: none; }
+.legal a:hover { color: #EE0; text-decoration: underline; }
+
+.clear { clear: both; }
View
47 element_tabs/tabs.txt
@@ -0,0 +1,47 @@
+[
+
+{title, "Tabs Element"},
+
+{see_also, [base]},
+
+{usage, "
+#tabs{
+ tag=tabsTag,
+ options=[
+ {collapsible, true},
+ {selected, 2},
+ {event, mouseover}
+ ],
+ tabs=[
+ #tab{ tag=tabTag, title=\"Tab 1\", body=[\"Tab one body...\"] },
+ #tab{ title=\"Tab 2\", body=#panel{ body=[\"Tab two body...\"] }},
+ #tab{ title=#span{ text=\"<Tab 3>\" },
+ url=\"some/path/to-tab-data\",
+ body=\"This body will be replaced by data from ajax call to above url.\" }
+ ]}
+"},
+
+{description, "The tabs element produces an jQuery UI tabs pane."},
+
+{attributes, [
+
+ {"tag", "Erlang term", "Tag used in event postbacks. Note: no events will be sent if tag is omitted."},
+ {"options", "list", "To Be Written. See jQuery UI docs: http://jqueryui.com/demos/tabs/"},
+ {"tabs", "list of tab elements", "Each tab should have a title and a body. If url is present, then the body is loaded from that url with ajax."}
+]},
+
+{events, [
+
+ {"tabs_event(Type, TabsTag, TabTag, TabIndex)", "
+ Called when a tab is selected, loaded, enabled, disabled, added, removed or shown.<br>
+ The Event provided is a tuple with event kind and the tag of the tabs pane.
+
+ <p>
+ Type is one of: tabsselect, tabsload, tabsshow, tabsadd, tabsremove, tabsenable, tabsdisable.<br>
+ Tag is the attribute provided for the tabs and tab elements respectively.
+ The event will not be triggered if the tabs tag is undefined.
+
+ "}
+]}
+
+].
View
28 element_tabs/web_samples_tabs.erl
@@ -0,0 +1,28 @@
+-module (web_samples_tabs).
+-include ("wf.inc").
+-compile(export_all).
+
+main() -> #template { file="./wwwroot/onecolumn.html", bindings=[
+ {'Group', learn},
+ {'Item', samples}
+]}.
+
+title() -> "Tabs Pane Example".
+headline() -> "Tabs Pane Example".
+right() -> linecount:render().
+
+body() -> [
+ #tabs{ tag=tabsTag,
+ options=[ {collapsible, true} ],
+ tabs=[
+ #tab{ tag=tabTag, title="Tab 1", body=["Tab one body..."] },
+ #tab{ title="Tab 2", body=#panel{ body=["Tab two body..."] }},
+ #tab{ title=[" ", #span{ text="<Tab 3>" }, " "],
+ body="Tab three body..." }
+ ]}
+].
+
+event(_) -> ok.
+
+tabs_event(_Evt, _TabsTag, _TabTag, _Index) ->
+ ok.
Please sign in to comment.
Something went wrong with that request. Please try again.