Commit d9a502cb authored by Sven Franck's avatar Sven Franck

WIP commit

parent c6cc2035
......@@ -41,8 +41,9 @@ html body select.invalid ~ .ui-invalid-label {
span.static {
padding: 0 .5em;
}
/* TODO: don't use important!!! */
/* TODO: don't use important!!!, padding is for missing clear button! */
.ui-mask-input {
padding-right: 2.375em;
background: 0 none !important;
}
.ui-mask-input.ui-focus {
......@@ -58,6 +59,7 @@ span.static {
line-height: 100%;
display: inline-block;
text-transform: capitalize;
padding-top: .5em;
}
.info:after {
content: "|";
......@@ -466,7 +468,7 @@ form .span_1 {
@media (max-width: 40em) {
.span_1, .span_2, form .span_1, form .span_2 {
width: 100%;
padding: 0;
padding: .25em 0;
}
.span_1 textarea {
width: 100%;
......
......@@ -18,6 +18,14 @@ body .ui-controlgroup-label legend {
html .listview .ui-li-divider {
font-size: 80%;
}
body .ui-fieldcontain .ui-controlgroup-label legend {
font-size: 95%;
}
@media (max-width: 40em) {
body .ui-fieldcontain .ui-controlgroup-label legend {
font-size: 92.5%;
}
}
/* Watermark NOTE: breaks links if ui-content is kept position:static
.ui-page-active:before,
......@@ -101,6 +109,8 @@ html .ui-page-theme-slapos-white .ui-table-wrapper.ui-table-wrapper-top .ui-plai
html .ui-page-theme-slapos-white .ui-table-wrapper.ui-table-wrapper-bottom .ui-btn,
html .ui-page-theme-slapos-white .ui-table-wrapper.ui-table-wrapper-bottom .ui-plain-text,
html .ui-page-theme-slapos-white .ui-controlbar .ui-btn,
html .ui-page-theme-slapos-white .ui-controlbar ~ .ui-controlgroup .ui-btn,
html .ui-page-theme-slapos-white .ui-collapsible h1 a.ui-btn,
html .ui-page-theme-slapos-white .ui-collapsible-set .ui-collapsible h1 a.ui-btn,
html .ui-page-theme-slapos-white .ui-table thead a.ui-btn,
html .ui-page-theme-slapos-white .ui-table thead label,
......@@ -117,6 +127,7 @@ html .ui-page-theme-slapos-white .ui-table-wrapper,
html .ui-page-theme-slapos-white .ui-popup .ui-header,
html .ui-page-theme-slapos-white .ui-popup .ui-footer,
html .ui-page-theme-slapos-white .ui-table-wrapper .ui-btn,
html .ui-page-theme-slapos-white .ui-collapsible h1 a.ui-btn,
html .ui-page-theme-slapos-white .ui-collapsible-set .ui-collapsible h1 a.ui-btn {
background-image: -webkit-gradient(linear, left top, left bottom, from( #fff ), to( #f1f1f1 ));
background-image: -webkit-linear-gradient( #fff , #f1f1f1 );
......@@ -310,7 +321,8 @@ html .ui-page-theme-slapos-white table tr th:hover ~ td,
/* controlbars */
html .ui-page-theme-slapos-white .ui-controlbar .ui-btn:hover,
/* tabs */
html .ui-page-theme-slapos-white .ui-collapsible-set .ui-collapsible h1 a.ui-btn:hover {
html .ui-page-theme-slapos-white .ui-collapsible-set .ui-collapsible h1 a.ui-btn:hover,
html .ui-page-theme-slapos-white .ui-collapsible h1 a.ui-btn:hover{
background-color: #e8e8e8;
background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#fff), to(#e8e8e8));
background: -webkit-linear-gradient(bottom, #fff, #e8e8e8);
......@@ -356,6 +368,8 @@ html .ui-page-theme-slapos-black .ui-table-wrapper.ui-table-wrapper-top .ui-plai
html .ui-page-theme-slapos-black .ui-table-wrapper.ui-table-wrapper-bottom .ui-btn,
html .ui-page-theme-slapos-black .ui-table-wrapper.ui-table-wrapper-bottom .ui-plain-text,
html .ui-page-theme-slapos-black .ui-controlbar .ui-btn,
html .ui-page-theme-slapos-black .ui-controlbar ~ .ui-controlgroup .ui-btn,
html .ui-page-theme-slapos-black .ui-collapsible h1 a.ui-btn,
html .ui-page-theme-slapos-black .ui-collapsible-set .ui-collapsible h1 a.ui-btn,
html .ui-page-theme-slapos-black .ui-table thead a.ui-btn,
html .ui-page-theme-slapos-black .ui-table thead label,
......@@ -373,6 +387,7 @@ html .ui-page-theme-slapos-black .ui-table-wrapper,
html .ui-page-theme-slapos-black .ui-popup .ui-header,
html .ui-page-theme-slapos-black .ui-popup .ui-footer,
html .ui-page-theme-slapos-black .ui-table-wrapper .ui-btn,
html .ui-page-theme-slapos-black .ui-collapsible h1 a.ui-btn
html .ui-page-theme-slapos-black .ui-collapsible-set .ui-collapsible h1 a.ui-btn {
background-image: -webkit-gradient(linear, left top, left bottom, from( #444 ), to( #222 ));
background-image: -webkit-linear-gradient( #444 , #222 );
......@@ -539,11 +554,13 @@ html body .ui-header .ui-group-theme-slapos-black .ui-btn:hover,
font-weight: normal;
line-height: 325% /* could also be 3.25em, but this is more flexible, while being crap! */;
}
/* Button special handling: listitem */
/* Button special handling: listitem , collapsibles */
.ui-page-theme-slapos-black .ui-panel-inner li .ui-btn,
html .ui-bar-slapos-black .ui-panel-inner li .ui-btn,
html .ui-body-slapos-black .ui-panel-inner li .ui-btn,
html .ui-body-slapos-black .ui-panel-inner li.ui-li-static,
html body .ui-panel-inner .ui-collapsible .ui-collapsible-heading a.ui-btn.ui-collapsible-heading-toggle,
html body .ui-panel-inner .ui-group-theme-slapos-black .ui-collapsible .ui-collapsible-heading a.ui-btn.ui-collapsible-heading-toggle,
html body .ui-panel-inner li .ui-btn.ui-btn-slapos-black,
html body .ui-panel-inner li .ui-group-theme-slapos-black .ui-btn,
[class*="ui-group-theme-"] .ui-panel-inner li .ui-btn.ui-btn-slapos-black,
......@@ -551,6 +568,8 @@ html body .ui-panel-inner li .ui-group-theme-slapos-black .ui-btn,
.ui-page-theme-slapos-black .ui-panel-inner li .ui-btn:visited,
html .ui-bar-slapos-black .ui-panel-inner li .ui-btn:visited,
html .ui-body-slapos-black .ui-panel-inner li .ui-btn:visited,
html body .ui-panel-inner .ui-collapsible .ui-collapsible-heading a.ui-btn.ui-collapsible-heading-toggle:visited,
html body .ui-panel-inner .ui-group-theme-slapos-black .ui-collapsible .ui-collapsible-heading a.ui-btn.ui-collapsible-heading-toggle:visited
html body .ui-panel-inner li .ui-btn.ui-btn-slapos-black:visited,
html body .ui-panel-inner li .ui-group-theme-slapos-black .ui-btn:visited,
[class*="ui-group-theme-"] .ui-panel-inner li .ui-btn.ui-btn-slapos-black:visited {
......@@ -583,6 +602,7 @@ html .ui-page-theme-slapos-black table tr th:hover ~ td,
/* controlbars */
html .ui-page-theme-slapos-black .ui-controlbar .ui-btn:hover,
/* tabs */
html .ui-page-theme-slapos-black .ui-collapsible h1 a.ui-btn:hover
html .ui-page-theme-slapos-black .ui-collapsible-set .ui-collapsible h1 a.ui-btn:hover {
background: #222;
background-image: -webkit-gradient(linear, left top, left bottom, from( #444 ), to( #222 ));
......@@ -692,6 +712,8 @@ html .ui-page-theme-slapos-white .ui-table-wrapper .ui.btn.ui-btn-active,
html .ui-page-theme-slapos-white .ui-controlbar .ui-btn:active,
html .ui-page-theme-slapos-white .ui-controlbar .ui-btn.ui-btn-active,
/* Tab Buttons */
html .ui-page-theme-slapos-white .ui-collapsible h1 a.ui-btn:active,
html .ui-page-theme-slapos-white .ui-collapsible h1 a.ui-btn.ui-btn-active,
html .ui-page-theme-slapos-white .ui-collapsible-set .ui-collapsible h1 a.ui-btn:active,
html .ui-page-theme-slapos-white .ui-collapsible-set .ui-collapsible h1 a.ui-btn.ui-btn-active,
/* black */
......@@ -737,6 +759,8 @@ html table.ui-table tbody tr.linkable:hover th:not(.ui-no-result),
html .ui-page-theme-slapos-black .ui-controlbar .ui-btn:active,
html .ui-page-theme-slapos-black .ui-controlbar .ui-btn.ui-btn-active,
/* Tab Buttons */
html .ui-page-theme-slapos-black .ui-collapsible h1 a.ui-btn:active,
html .ui-page-theme-slapos-black .ui-collapsible h1 a.ui-btn.ui-btn-active
html .ui-page-theme-slapos-black .ui-collapsible-set .ui-collapsible h1 a.ui-btn:active,
html .ui-page-theme-slapos-black .ui-collapsible-set .ui-collapsible h1 a.ui-btn.ui-btn-active,
/* LOADER */
......@@ -884,13 +908,22 @@ html .ui-header .ui-last-wrap .ui-controlgroup-controls .ui-btn {
/* TODO: form fields should auto-expand to full width */
@media (min-width: 40em) {
.ui-fieldcontain label:not(.ui-btn),
.ui-fieldcontain div.ui-controlgroup-label,
.ui-fieldcontain div.ui-controlgroup,
.ui-fieldcontain div.ui-input-text,
.ui-fieldcontain div.ui-input-search,
.ui-fieldcontain div.ui-select {
display: inline-block;
vertical-align: middle;
}
.ui-fieldcontain label:not(.ui-btn) {
.ui-fieldcontain div.ui-controlgroup-label,
.ui-fieldcontain div.ui-controlgroup {
vertical-align: top;
padding-top: .5em;
padding-bottom: .5em;
}
.ui-fieldcontain label:not(.ui-btn),
.ui-fieldcontain div.ui-controlgroup-label {
/* width: 46% *//*40% not working either*/
max-width: 30%;
min-width: 30%;
......@@ -902,6 +935,9 @@ html .ui-header .ui-last-wrap .ui-controlgroup-controls .ui-btn {
.ui-fieldcontain div.ui-select {
width: 46%;
}
.ui-fieldcontain div.ui-controlgroup {
width: 57%;
}
.ui-fieldcontain div.ui-select .ui-btn {
-webkit-box-shadow: none;
-moz-box-shadow: none;
......@@ -1683,7 +1719,7 @@ html.ui-nosvg .ui-btn-icon-left:after {
bottom: 0;
top: 0;
box-shadow: none !important;
font-size: 1.25em;
font-size: 1.2em;
height: 100%;
left: 0.25em;
line-height: 1em;
......@@ -1693,7 +1729,7 @@ html.ui-nosvg .ui-btn-icon-left:after {
position: absolute;
text-align: center;
width: 22px;
top: 19%; /* no friend... */
top: 29%; /* no friend... */
}
.ui-listview li span.ui-li-icon-custom:after {
background: none repeat scroll 0 center transparent;
......@@ -1786,6 +1822,8 @@ html body .ui-input-search .ui-input-clear:active {
-webkit-box-shadow: none /* iOS3 */ !important;
box-shadow: none /* iOS3 */ !important;
}
/* ================================== collapsibles ============================ */
/* collapsibles - fix default 200% font-size set by browser */
html .ui-collapsible h1 {
font-size: 100%; /* clean this up... should not be necessary */
......@@ -1793,6 +1831,17 @@ html .ui-collapsible h1 {
html .ui-collapsible .ui-collapsible-content .ui-listview li a.ui-btn {
font-weight: normal;
}
html .ui-collapsible {
margin: 0;
}
html .ui-collapsible h1 {
font-size: .9em;
}
html .ui-panel-inner .ui-collapsible h1 a.ui-collapsible-heading-toggle {
padding: 0.4em 2.75em !important;
font-weight: bold;
}
/* selectmenu - crap... */
.ui-select .ui-btn-corner-all,
......
......@@ -65,7 +65,7 @@
"generate": "widget",
"type": "controlbar",
"property_dict": {"slot": 1},
"children": [{"type": "a", "direct": {"className": "action ui-btn ui-btn-icon-left ui-icon-sitemap ui-shadow ui-corner-all","href":"#global-popup"},"attributes": {"data-rel":"popup"},"logic": {"text":"Group"}}]
"children": [{"type": "a", "direct": {"className": "ui-btn ui-btn-icon-left action ui-icon-sitemap ui-shadow ui-corner-all","href":"#cedric"},"attributes": {"data-action": "show_tree", "data-rel":"panel"},"logic": {"text":"Group"}}]
}, {
"generate": "widget",
"type": "table",
......
......@@ -128,12 +128,36 @@
"type": "listview",
"class_list": null,
"theme": "slapos-black",
"form": null,
"property_dict": {},
"children": [
{"type": "divider", "center": {"text": [{"type": "h1", "text": "Development", "text_i18n":"global_dict.dev"}]}},
{"type": "item", "href": "#test_page_module", "left": {"icon": "dashboard"}, "center": {"text": [{"type": "h1", "text": "Test", "text_i18n": "global_dict.test"}, {"type": "p", "text": "Test Environment", "text_i18n": "global_dict.test_environment"}]}}
]
{"type": "divider", "center": {"text": [{"type": "h1", "text": "Development", "text_i18n":"global_dict.dev"}]}}
]
}, {
"generate": "widget",
"type": "collapsible",
"property_dict": {
"theme": "slapos-black",
"content_theme": "slapos-black",
"text": "Test",
"text_i18n": "global_dict.test",
"inset": false,
"collapsed_icon": "dashboard"
},
"children": [{
"generate": "widget",
"type": "listview",
"class_list": null,
"theme": "slapos-black",
"form": null,
"property_dict": {},
"children": [
{"type": "item", "href": "#", "left": {"icon": "video-camera"}, "center": {"text": [{"type": "h1", "text": "Cases", "text_i18n": "global_dict.cases"}, {"type": "p", "text": "<<nothing to see here>>", "text_i18n": "global_dict.test_cases"}]}},
{"type": "item", "href": "#", "left": {"icon": "code-fork"}, "center": {"text": [{"type": "h1", "text": "Nodes", "text_i18n": "global_dict.nodes"}, {"type": "p", "text": "<<nothing to see here>>", "text_i18n": "global_dict.test_nodes"}]}},
{"type": "item", "href": "#test_page_module", "left": {"icon": "file-o"}, "center": {"text": [{"type": "h1", "text": "Pages", "text_i18n": "global_dict.pages"}, {"type": "p", "text": "Create and run test pages locally", "text_i18n": "global_dict.test_pages"}]}},
{"type": "item", "href": "#", "left": {"icon": "folder-o"}, "center": {"text": [{"type": "h1", "text": "Reports", "text_i18n": "global_dict.reports"}, {"type": "p", "text": "<<nothing to see here>>", "text_i18n": "global_dict.test_results"}]}},
{"type": "item", "href": "#", "left": {"icon": "signal"}, "center": {"text": [{"type": "h1", "text": "Results", "text_i18n": "global_dict.results"}, {"type": "p", "text": "<<nothing to see here>>", "text_i18n": "global_dict.test_reports"}]}},
{"type": "item", "href": "#", "left": {"icon": "cog"}, "center": {"text": [{"type": "h1", "text": "Suites", "text_i18n": "global_dict.suites"}, {"type": "p", "text": "<<nothing to see here>>", "text_i18n": "global_dict.test_suites"}]}}
]
}]
}, {
"type": "a",
"direct": {"className": "unenhanced ui-btn", "href":"http://nexedi.com", "external": true },
......
......@@ -15,21 +15,7 @@
"generate": "gadget",
"type": "listbox",
"href": "person_overview"
},{
"generate": "widget",
"type": "panel",
"id": "cedric",
"theme": "slapos-white",
"property_dict": {
"close": null
},
"children": [{
"type": "p",
"direct": {},
"attributes": {},
"logic": {"text": "Hello Cedric"}
}]
}],
}],
"new": [
{
"generate": "gadget",
......
......@@ -15,8 +15,21 @@
"generate": "gadget",
"type": "listbox",
"href": "computer_overview"
}
],
} ,{
"generate": "widget",
"type": "panel",
"id": "cedric",
"theme": "slapos-white",
"property_dict": {
"close": null
},
"children": [{
"type": "p",
"direct": {},
"attributes": {},
"logic": {"text": "Hello Cedric"}
}]
}],
"new": [
{
"generate": "gadget",
......
......@@ -93,7 +93,7 @@
"enabled": true,
"editable": true,
"external_validator": null,
"required": null,
"required": true,
"preserve_whitespace": null,
"unicode": null,
"maximum_length": null,
......@@ -115,9 +115,9 @@
"widget": {
"id": "description",
"title": "Description",
"title_i18n": "portal_type_dict.person_dict.field_dict.description.title",
"title_i18n": "portal_type_dict.test_page_dict.field_dict.description.title",
"description": "The description of this test page.",
"description_i18n": "portal_type_dict.person_dict.field_dict.description.description",
"description_i18n": "portal_type_dict.test_page_dict.field_dict.description.description",
"alternate_name": "description",
"css_class": null,
"hidden": null,
......@@ -253,7 +253,7 @@
"enabled": true,
"editable": true,
"external_validator": null,
"required": null,
"required": true,
"preserve_whitespace": null,
"unicode": null,
"maximum_length": null,
......@@ -356,14 +356,14 @@
"widget": {
"id": "state",
"title": "State",
"title_i18n": "portal_type_dict.person_dict.field_dict.state.title",
"title_i18n": "portal_type_dict.test_page_dict.field_dict.state.title",
"description": "The state of this test page.",
"description_i18n": "portal_type_dict.person_dict.field_dict.state.description",
"description_i18n": "portal_type_dict.test_page_dict.field_dict.state.description",
"alternate_name": "state",
"default_value": null,
"css_class": null,
"hidden": null,
"items": null,
"items": "getStateOfTestPage",
"size": null,
"extra": null,
"extra_per_item": null
......@@ -389,5 +389,94 @@
"i18n": "validation_dict.option_not_available"
}
}
},
"text_content": {
"type":"TextareaField",
"widget": {
"id": "text_content",
"title": "Test Instructions",
"title_i18n": "portal_type_dict.test_page_dict.field_dict.text_content.title",
"description": "The test instructions for this test page.",
"description_i18n": "portal_type_dict.test_page_dict.field_dict.text_content.description",
"alternate_name": "text_content",
"css_class": null,
"hidden": null,
"width":null,
"height":null,
"extra":null
},
"properties": {
"enabled": true,
"editable": true,
"external_validator": null,
"required": null,
"preserve_whitespace": null,
"unicode": null,
"maximum_lines": null,
"maximum_length_of_line": null,
"maximum_characters": null
},
"message": {
"external_validator_failed": {
"message": "The input failed the external validator.",
"i18n": "validation_dict.external"
},
"required_not_found": {
"message": "Input required but not found.",
"i18n": "validation_dict.required"
},
"too_many_lines": {
"message": "You have entered too many lines.",
"i18n": "validation_dict.too_many_lines"
},
"line_too_long": {
"message": "One or more lines you have entered are too long.",
"i18n": "validation_dict.too_long_lines"
},
"too_long": {
"message": "You have entered too many characters.",
"i18n": "validation_dict.too_many_chars"
}
}
},
"content_type": {
"type":"RadioField",
"widget": {
"id": "content_type",
"title": "Content Type",
"title_i18n": "portal_type_dict.test_page_dict.field_dict.content_type.title",
"description": "The content type of the test instructions.",
"description_i18n": "portal_type_dict.test_page_dict.field_dict.content_type.description",
"alternate_name": "content_type",
"css_class": null,
"hidden": null,
"size": 1,
"items": "getContentFormat",
"select_first_item": true,
"extra_per_item": null,
"extra": null
},
"properties": {
"enabled": true,
"editable": true,
"external_validator": null,
"required": null,
"preserve_whitespace": null,
"unicode": null
},
"messages": {
"external_validator_failed": {
"message": "The input failed the external validator.",
"i18n": "validation_dict.external"
},
"required_not_found": {
"message": "Input is required but no input given.",
"i18n": "validation_dict.required"
},
"unknown_selection": {
"message":"You selected on option not on the menu",
"i18n": "validation_dict.option_not_available"
}
}
}
}
\ No newline at end of file
......@@ -14,8 +14,133 @@
"generate": "gadget",
"type": "listbox",
"href": "test_page_overview"
}, {
"generate": "widget",
"type": "panel",
"id": "test_page_detail_search",
"theme": "slapos-white",
"property_dict": {
"close": null,
"local": true
},
"children": []
}
]
],
"new": [{
"generate": "gadget",
"type": "fieldlist",
"href": "test_page_new"
}]
}
}, {
"generate": "widget",
"type": "page",
"title": "Test Page",
"title_i18n": "portal_type_dict.test_page_dict.text_dict.page",
"theme": "slapos-white",
"view_dict": {
"default": [{
"generate": "widget",
"type": "collapsibleset",
"property_dict": {
"direction": "horizontal",
"inset": true
},
"children": [{
"generate": "widget",
"type": "collapsible",
"property_dict": {
"collapsed": false,
"text": "View",
"text_i18n": "page_dict.test_page_dict.text_dict.view"
},
"children": [{
"generate": "gadget",
"type":"fieldlist",
"href": "test_page_view"
}]
},{
"generate": "widget",
"type": "collapsible",
"property_dict": {
"text": "Edit",
"text_i18n": "page_dict.test_page_dict.text_dict.edit"
},
"children": []
},{
"generate": "widget",
"type": "collapsible",
"property_dict": {
"text": "Test Report",
"text_i18n": "page_dict.test_page_dict.text_dict.test_report"
},
"children": []
},{
"generate": "widget",
"type": "collapsible",
"property_dict": {
"text": "Edit Slideshow",
"text_i18n": "page_dict.test_page_dict.text_dict.edit_slideshow"
},
"children": []
}, {
"generate": "widget",
"type": "collapsible",
"property_dict": {
"text": "Edit Test",
"text_i18n": "page_dict.test_page_dict.text_dict.edit_test"
},
"children": []
}, {
"generate": "widget",
"type": "collapsible",
"property_dict": {
"text": "Related Documents",
"text_i18n": "page_dict.test_page_dict.text_dict.related"
},
"children": []
}, {
"generate": "widget",
"type": "collapsible",
"property_dict": {
"text": "Contents",
"text_i18n": "page_dict.test_page_dict.text_dict.contents"
},
"children": []
}, {
"generate": "widget",
"type": "collapsible",
"property_dict": {
"text": "Local Roles",
"text_i18n": "page_dict.test_page_dict.text_dict.roles"
},
"children": []
}, {
"generate": "widget",
"type": "collapsible",
"property_dict": {
"text": "Consistency",
"text_i18n": "page_dict.test_page_dict.text_dict.consistency"
},
"children": []
}, {
"generate": "widget",
"type": "collapsible",
"property_dict": {
"text": "History",
"text_i18n": "page_dict.test_page_dict.text_dict.history"
},
"children": []
}, {
"generate": "widget",
"type": "collapsible",
"property_dict": {
"text": "Metadata",
"text_i18n": "page_dict.test_page_dict.text_dict.metadata"
},
"children": []
}]
}]
}
}
]
......
{
"portal_type_source": "Test Page",
"portal_type_title": "test",
"portal_type_fields": "test_page_fieldlist",
"initial_query": {"include_docs": true, "limit":[0,1]},
"form": true,
"view": "web_view",
"property_dict": {
"dynamic_children": [0],
"requires_authentication": true,
"depends_on": "login_state",
"wrap_gadget": 2,
"submit_to": "#test_page_module/__id__"
},
"scheme": [
{
"position": "left",
"fieldlist": [
{"field": "title"},
{"field": "short_title"}
]
}, {
"position": "right",
"fieldlist": [
{"field": "description"}
]
}
],
"children": [{
"generate": "widget",
"type": "form",
"class_list": "responsive",
"property_dict": {
"editable": true,
"secure": "default",
"secret_hash": "foo",
"public_key": "6Ldpb-oSAAAAAGwriKpk4ol1n4yjN_as6M4xv0zA"
},
"children": [{
"generate": "widget",
"type": "controlgroup",
"class_list": "center",
"property_dict": {
"direction": "horizontal"
},
"children": [
{"type": "input", "direct": {"value": "Submit", "className": "action translate"}, "attributes": {"type": "submit", "data-action":"new", "data-icon":"save", "data-i18n":"[value]portal_type_dict.test_page_dict.text_dict.add", "data-theme": "slapos-black"}}
]
}]
}
]
}
......@@ -6,7 +6,7 @@
"form": true,
"view": "web_view",
"property_dict": {
"dynamic_children": [1],
"dynamic_children": [2],
"requires_authentication": true,
"depends_on": "login_state",
"wrap_gadget": 2,
......@@ -33,7 +33,7 @@
"search": {
"text": "Search Test Pages",
"text_i18n": "portal_type_dict.test_page_dict.text_dict.search",
"info_list": ["records", "filter", "selected"]
"info_list": ["records", "filter"]
},
"allow_new": true
},
......@@ -68,12 +68,23 @@
],
"children": [{
"type": "a",
"direct": {"className":"translate ui-btn ui-icon-edit ui-btn-icon-left ui-corner-all ui-btn-inline ui-btn-slapos-black ui-link", "href": "#test_page/new"},
"direct": {"className":"translate ui-btn ui-icon-edit ui-btn-icon-left ui-corner-all ui-btn-inline ui-btn-slapos-black ui-link", "href": "#test_page_module/new"},
"attributes": {
"data-i18n": "[value]portal_type_dict.test_page_dict.text_dict.add",
"data-i18n": "portal_type_dict.test_page_dict.text_dict.add",
"data-icon": "edit"
},
"logic": {"text":"Add a test page"}
"logic": {"text":"Add Test Page"}
}, {
"generate": "widget",
"type": "controlgroup",
"class_list": "right",
"property_dict": {
"direction": "horizontal"
},
"children": [
{"type": "a", "direct": {"href": "#test_page_detail_search", "className": "translate ui-btn ui-icon-sitemap ui-btn-icon-notext action"}, "attributes": {"data-action": "group", "data-rel":"panel", "data-i18n":"portal_type_dict.test_page_dict.text_dict.group"}, "logic": {"text":"Group Record Filter"}},
{"type": "a", "direct": {"href": "#test_page_detail_search", "className": "translate ui-btn ui-icon-filter ui-btn-icon-notext action"}, "attributes": {"data-action": "filter", "data-rel":"panel", "data-i18n":"portal_type_dict.test_page_dict.text_dict.filter"}, "logic": {"text":"Detail Search"}}
]
}, {
"generate": "widget",
"type": "table",
......
[
{
"_id": "foo",
"short_title": "bar",
"portal_type": "Test Page"
},
{
"_id": "baz",
"short_title": "bam",
"portal_type": "Test Page"
}
]
\ No newline at end of file
{
"portal_type_source": "Test Page",
"portal_type_title": "test",
"portal_type_fields": "test_page_fieldlist",
"initial_query": {"include_docs": true, "limit":[0,1]},
"form": true,
"view": "web_view",
"property_dict": {
"dynamic_children": [0],
"requires_authentication": true,
"depends_on": "login_state",
"wrap_gadget": 2
},
"scheme": [
{
"position": "left",
"fieldlist": [
{"field": "_id", "overrides": {"properties":{"editable": false}}},
{"field": "reference"},
{"field": "title"},
{"field": "short_title"}
]
}, {
"position": "right",
"fieldlist": [
{"field": "version"},
{"field": "language"},
{"field": "index"},
{"field": "state"},
{"field": "content_type"}
]
}, {
"position": "center",
"fieldlist": [
{"field": "description"}
]
}
],
"children": [{
"generate": "widget",
"type": "form",
"class_list": "responsive",
"property_dict": {
"editable": true,
"secure": "default",
"secret_hash": "foo",
"public_key": "6Ldpb-oSAAAAAGwriKpk4ol1n4yjN_as6M4xv0zA"
},
"children": [{
"generate": "widget",
"type": "controlgroup",
"class_list": "center",
"property_dict": {
"direction": "horizontal"
},
"children": [
{"type": "input", "direct": {"value": "Submit", "className": "action translate"}, "attributes": {"type": "submit", "data-action":"update", "data-icon":"check", "data-i18n":"[value]portal_type_dict.test_page_dict.text_dict.update", "data-theme": "slapos-black"}}
]
}]
}
]
}
......@@ -48,7 +48,6 @@
*/
ERP5Storage.prototype._getDoc = function (command, hal, param, options) {
var fetch, force, item = (param || {})._id || (hal._links.me || {}).href;
if (!item) {
command.error(401);
}
......@@ -176,14 +175,15 @@
* @param {Object} options The command options
*/
ERP5Storage.prototype.post = function (command, metadata, options) {
var that = this;
var hal, that = this;
return that._getBase()
.then(function (site_hal) {
return that._getDoc(command, site_hal, undefined, options);
hal = site_hal;
return that._getDoc(command, hal, undefined, options);
})
.then(function (result) {
var key, custom_action = result._actions[options._action],
post_action = custom_action || result._actions.add,
post_action = custom_action || hal._actions.add,
data = new FormData();
for (key in metadata) {
......@@ -262,7 +262,6 @@
}
}
}
return jIO.util.ajax({
"type": put_action.method,
"url": put_action.href,
......
......@@ -319,7 +319,7 @@
metadata._id = response.id;
method = "put";
} else {
// TODO: master 404/offline handling
// TODO: no-access master 404/offline handling
}
for (index = 0; index < length; index += 1) {
......@@ -347,6 +347,7 @@
// setup master storage or all
if (master !== undefined) {
console.log("INTO MASTER")
priority_list[0] = success(
command.storage(storages[master]).put(metadata, option)
);
......@@ -367,7 +368,11 @@
var subindex;
switch (true) {
case response.status >= 500: command.error(response); break;
// NOTE: this means if the master is 503, nothing will be saved on
// the slave? Is this what I would want?
case response.status >= 500:
// command.error(response);
// break;
// HACK: why does 204 return status code 0 on promises?
case response.status === 0:
default:
......
......@@ -574,14 +574,16 @@ html table tr td .ui-radio label.ui-btn {
white-space: pre-line;
line-height: 0;
display: block;
margin: 1em auto;
margin: 0 auto 1em;
}
/* TODO: no fan */
.ui-controlbar ~ a.ui-btn {
.ui-controlbar ~ a.ui-btn,
.ui-controlbar ~ .ui-controlgroup .ui-btn {
float: left;
padding-top: .4em;
padding-bottom: .4em;
line-height: 1.4em;
margin: 0 auto 1em;
}
.ui-controlbar > * {
display: inline-block;
......@@ -593,7 +595,7 @@ html table tr td .ui-radio label.ui-btn {
}
.ui-controlbar .ui-input-search {
display: block;
margin: 0.5em auto;
margin: 0 auto;
}
/* align multiple controlgroups in a controlbar on wide screens */
html body div.ui-content .ui-controlbar .ui-controlgroup {
......@@ -634,25 +636,21 @@ html body .ui-controlbar > .ui-btn:not(.ui-input-clear, .ui-input-action) {
width: 60%;
display: inline-block;
padding: 0 3%;
}
.ui-controlbar,
.ui-controlbar ~ a.ui-btn,
.ui-controlbar ~ .ui-controlgroup .ui-btn {
margin: 0 auto;
}
}
/* custom bar */
.ui-bar-plain {
padding: .7em 1em;
border: 1px solid;
font-size: 1em;
}
/* search filter bar?
.ui-controlbar {
margin-top: 1em;
margin-bottom: 1em;
}
*/
.ui-controlbar .ui-plain-text {
display: block;
font-size: 80%;
......
......@@ -5,6 +5,7 @@
* 4. add checkbox-on/off, radio on/off icons to fontawesome icons
* 5. add JQM compat at end
* 6. replace all updated name in code...
* 7. JQM uses carat-t/b/l/r, add those to caret-left/right/top/bottom
*/
/*!
......@@ -787,16 +788,20 @@ html .ui-icon-google-plus:after {
html .ui-icon-money:after {
content: "\f0d6";
}
html .ui-icon-caret-down:after {
html .ui-icon-caret-down:after,
html .ui-icon-carat-d:after {
content: "\f0d7";
}
html .ui-icon-caret-up:after {
html .ui-icon-caret-up:after,
hmtl .ui-icon-carat-u:after{
content: "\f0d8";
}
html .ui-icon-caret-left:after {
html .ui-icon-caret-left:after,
html .ui-icon-carat-l:after {
content: "\f0d9";
}
html .ui-icon-caret-right:after {
html .ui-icon-caret-right:after,
html .ui-icon-carat-r:after {
content: "\f0da";
}
html .ui-icon-columns:after {
......
......@@ -31,6 +31,20 @@
map = {};
map.options = {
"getStateOfTestPage": function () {
return [
{"text": "Draft", "text_i18n": null, "class": "translate", "value": "draft"},
{"text": "Published Alive", "text_i18n": null, "class": "translate", "value": "pubished/alive"}
];
},
"getContentFormat": function () {
return [
{"text": "HTML", "text_i18n": null, "class": "translate", "value":"text/html"},
{"text": "Plain Text", "text_i18n": null, "class": "translate", "value":"text/plain"},
{"text": "Structured Text", "text_i18n": null, "class": "translate", "value":"text/structured"},
{"text": "reStructuredText", "text_i18n": null, "class": "translate", "value":"text/x-rst"}
]
},
"getTicketType": function () {
return [
{"text": "", "text_i18n":null, "class":"translate", "value":""},
......@@ -652,8 +666,9 @@
})
.fail(function (error) {
switch (error.status) {
case 408: util.loader("", "status_dict.timeout", "time"); break;
default: util.loader("", "status_dict.error", "ban-circle"); break;
case 408: util.loader("", "status_dict.timeout", "clock-o"); break;
case 400: util.loader("", "validation_dict.general", "ban"); break;
default: util.loader("", "status_dict.error", "ban"); break;
}
});
},
......@@ -672,7 +687,8 @@
.fail(function (error) {
switch (error.status) {
case 408: util.loader("", "status_dict.timeout", "time"); break;
default: util.loader("", "status_dict.error", "ban-circle"); break;
case 400: util.loader("", "validation_dict.general", "ban"); break;
default: util.loader("", "status_dict.error", "ban"); break;
}
});
},
......@@ -693,7 +709,7 @@
.fail(function (error) {
switch (error.status) {
case 408: util.loader("", "status_dict.timeout", "time"); break;
default: util.loader("", "status_dict.error", "ban-circle"); break;
default: util.loader("", "status_dict.error", "ban"); break;
}
});
},
......@@ -722,7 +738,7 @@
util.loader("", "status_dict.ssl_revoked", "check");
})
.fail(function (error) {
util.loader("", "status_dict.ssl_no", "ban-circle");
util.loader("", "status_dict.ssl_no", "ban");
});
},
......@@ -742,7 +758,7 @@
app.navigate(obj, response);
})
.fail(function (error) {
util.loader("", "status_dict.ssl_error", "ban-circle");
util.loader("", "status_dict.ssl_error", "ban");
});
},
......@@ -780,7 +796,7 @@
* @param {object} obj Action Object
**/
"stop": function (obj) {
util.loader("", "status_dict.stopping", "ban-circle");
util.loader("", "status_dict.stopping", "ban");
storage.write(obj)
.then(function (response) {
util.loader("", "status_dict.success", "check");
......@@ -1069,7 +1085,7 @@
}
}
} else {
util.loader("", "status_dict.failed", "ban-circle");
util.loader("", "status_dict.failed", "ban");
}
// since we are jquerying, close (all) popups
......@@ -1575,7 +1591,7 @@
"mapFormField": function (form_id, spec, overrides, passed_value) {
var validation_list, class_list, element, type, prevail, clear, config,
field_value, skip, use_type, textarea_value, alternate_name,
option_list, i;
option_list, i, j, group, clone, cloned_checkboxradio;
// build config object
config = {};
......@@ -1651,6 +1667,22 @@
use_type = spec.type;
}
// options
// TODO: async... pff
option_list = null;
if (prevail.widget.items || spec.widget.items) {
option_list = map.options[prevail.widget.items || spec.widget.items]();
if (field_value) {
for (i = 0; i < option_list.length; i += 1) {
if (option_list[i].value === field_value) {
option_list[i].selected = "selected";
}
}
}
}
// type-related validation, element and type definition
switch (use_type) {
case "ImageField":
......@@ -1677,6 +1709,13 @@
element = "input";
type = "checkbox";
break;
case "RadioField":
element = "input";
type = "radio";
if (option_list.length > 1) {
clone = true;
}
break;
case "MultiListField":
case "ListField":
case "ParallelListField":
......@@ -1726,21 +1765,6 @@
validation_list = undefined;
}
// options
// TODO: async... pff
option_list = null;
if (prevail.widget.items || spec.widget.items) {
option_list = map.options[prevail.widget.items || spec.widget.items]();
if (field_value) {
for (i = 0; i < option_list.length; i += 1) {
if (option_list[i].value === field_value) {
option_list[i].selected = "selected";
}
}
}
}
// define alternate name here
alternate_name = prevail.widget.alternate_name ||
spec.widget.alternate_name;
......@@ -1773,7 +1797,7 @@
"text": textarea_value,
"readonly": (prevail.properties.editable === false ||
spec.properties.editable === false) ? "readonly" : undefined,
"options": option_list,
"options": clone ? undefined : option_list,
"label": prevail.widget.title || spec.widget.title,
"label_i18n": prevail.widget.title_i18n || spec.widget.title_i18n,
"title": prevail.widget.description || spec.widget.description,
......@@ -1784,7 +1808,39 @@
} else {
util.error("mapFormField: Missing field definition");
}
return config;
// radio and checkbox groups require an extra loop
if (clone) {
group = {
"generate": "widget",
"type": "controlgroup",
"property_dict": {
"label": {
"text": config.logic.label,
"text_i18n": config.logic.label_i18n,
"title": config.logic.title,
"title_i18n": config.logic.title_i18n
}
},
"children": []
};
for (j = 0; j < option_list.length; j += 1) {
cloned_checkboxradio = {
"type": "input",
"logic": util.cloneObject(config.logic),
"attributes": util.cloneObject(config.attributes),
"direct": util.cloneObject(config.direct)
}
cloned_checkboxradio.direct.id += "_" + j;
cloned_checkboxradio.logic.label = option_list[j].text;
cloned_checkboxradio.logic.label_i18n = option_list[j].text_i18n;
cloned_checkboxradio.logic.value = option_list[j].value;
cloned_checkboxradio.attributes["data-i18n"] = option_list[j].text_i18n;
group.children.push(cloned_checkboxradio);
}
}
return group || config;
}
};
......@@ -2115,7 +2171,7 @@
// dynamic element being updated - 0 results need placeholder
if (element.dynamic && no_result) {
if (update) {
if (update && !no_result) {
content.appendChild(no_content(props.no_items));
} else {
content.querySelector("tbody").appendChild(no_content(props.no_items));
......@@ -2756,6 +2812,303 @@
return breadcrumb;
};
/* ********************************************************************** */
/* JQM Collapsible */
/* ********************************************************************** */
/**
* Generate a JQM collapsible
*
* {
* "generate": "collapsible",
* "property_dict": {
* "href": "#",
* "id": null,
* "class_list": null,
* "has_form": null,
* "inset": true,
* "theme": null,
* "content_theme": null,
* "corners": null,
* "collapsed": false,
* "collapsed_icon": "plus",
* "expanded_icon": "minus",
* "iconpos": "left",
* "mini": null,
* "text": "Collapsible Heading",
* "text_i18n": null,
* "title": "Click to expand",
* "title_i18n": null,
* "dynamic": null
* },
* "children": []
* }
*
* @method collapsible
* @param {Object} spec Configuration for controlgroup
* @param {String} spec.generate Type of widget to generate
* @param {Object} spec.property_dict Configuration of widget
* @param {String} [spec.property_dict.href="#"] Href of content to load
* @param {String} [spec.property_dict.id=null] Id to set on widget
* @param {String} [spec.property_dict.class_list=null] Classes to add
* @param {Boolean} [spec.property_dict.has_form=null] Widget contains a form
* @param {Boolean} [spec.property_dict.inset=true] Inset (Round corners)
* @param {String} [spec.property_dict.theme=null] Theme to set on widget
* @param {String} [spec.property_dict.content_theme=null] Theme for content
* @param {Boolean} [spec.property_dict.collapsed=true] Collapsible is open
* @param {String} [spec.property_dict.collapsed_icon="plus"] Opened Icon
* @param {String} [spec.property_dict.expanded_icon="minus"] Closed Icon
* @param {String} [spec.property_dict.iconpos="left"] Icon positioning
* @param {Boolean} [spec.property_dict.mini=false] Shrink widget size
* @param {Boolean} [spec.property_dict.corners=null] Corners to set
* @param {String} spec.property_dict.text="\u00A0" Collapsible Heading
* @param {String} [spec.property_dict.text_i18n=null] Translation Lookup
* @param {String} spec.property_dict.title="Click to expand" Tooltip
* @param {String} [spec.property_dict.title_i18n=null] Translation Lookup
* @param {Boolean} [spec.property_dict.dynamic=null] Dynamic (Rename/Remove)
* @param {Array} spec.children Content to display inside the widget
* @return collapsible
*/
factory.collapsible = function (spec) {
var config, state, direction, collapsible, heading, link, content,
collapsed, translate, content, theme, i, l, k, m, element, header, answer,
promise_list;
if (spec === undefined) {
util.error("Generate Collapsible: Missing config");
} else {
config = spec.property_dict || {};
direction = config.direction || "vertical";
state = spec.set_state;
collapsed = config.collapsed === false;
translate = config.text_i18n ? "translate " : " ";
theme = (config.theme + " ") || "inherit ";
promise_list = [];
collapsible = factory.element(
config.has_form ? "fieldset" : "div",
{
"className": "ui-collapsible " + (config.mini ? "ui-mini " : "") +
(!config.inset ? "" : "ui-collapsible-inset ui-corner-all ") +
(config.content_theme ? "ui-collapsible-themed-content " : " ") +
(collapsed ? " " : "ui-collapsible-collapsed ") +
(config.class_list || "")
},
{
"data-role": "collapsible",
"data-enhanced": true
},
{
"data-inset": config.inset || null,
"data-theme": config.theme || null,
"data-content-theme": config.content_theme || null,
"data-collapsed": config.collapsed || null,
"data-collapsed-icon": config.collapsed_icon || null,
"data-expanded-icon" : config.expanded_icon || null,
"data-iconpos": config.iconpos || null
}
);
header = factory.element(
config.hasForm ? "legend": "h1",
{
"className": "ui-collapsible-heading " +
(collapsed ? " " : "ui-collapsible-heading-collapsed")
}
);
link = factory.element(
"a",
{
"href": config.href || "#",
"className": "ui-collapsible-heading-toggle ui-btn " +
"ui-btn-icon-" + (config.iconpos || "left") + " " +
"ui-icon-" + (!config.collapsed ?
(config.collapsed_icon || "minus") :
(config.expanded_icon || "plus")) + " " + translate
},
{},
{
"data-i18n": config.text_i18n ? ("[Node]" + config.text_i18n) : null
}
);
link.appendChild(document.createTextNode(config.text || "\u00A0"));
link.appendChild(factory.element(
"span",
{"className": "ui-collapsible-heading-status " + translate},
{},
{
"data-i18n": config.title_i18n || null,
"text": config.title
}
));
header.appendChild(link);
// Dynamic - TODO: should this only work on a set?
if (config.dynamic) {
header.appendChild(factory.element(
"a",
{
"href":"#",
"className":"tab_action action ui-link ui-btn ui-icon-pencil" +
" ui-btn-icon-notext ui-shadow ui-corner-all ui-btn-" + theme
},
{
"role": "button",
"data-i18n": "global_dict.edit",
"data-action": "edit_tab"
},
{"text": "Edit"}
));
header.appendChild(factory.element(
"a",
{
"href":"#",
"className":"tab_action action ui-link ui-btn ui-icon-remove" +
" ui-btn-icon-notext ui-shadow ui-corner-all ui-btn-" + theme
},
{
"role": "button",
"data-i18n": "global_dict.delete",
"data-action": "delete_tab"
},
{"text": "Delete"}
));
}
collapsible.appendChild(header);
content = factory.element(
"div",
{
"className":"ui-collapsible-content ui-body-" + theme +
(!!config.collapsed ? " " : "ui-collapsible-content-collapsed")
},
{"aria-hidden": (config.collapsed ? false : true)}
);
// children
if (spec.children) {
for (i = 0, l = spec.children.length; i < l; i += 1) {
element = spec.children[i];
// reference to parent gadget
if (spec.reference) {
if (element.attributes !== undefined) {
element.attributes["data-reference"] = spec.reference;
} else {
element.reference = spec.reference;
}
}
answer = app.setContent(element);
if (answer.childElementCount === undefined) {
promise_list[i] = answer;
} else {
promise_list[i] = RSVP.resolve(answer);
}
}
}
return RSVP.all(promise_list)
.then(function(responses) {
for (k = 0, m = responses.length; k < m; k += 1) {
content.appendChild(responses[k]);
}
collapsible.appendChild(content);
return collapsible;
});
//return collapsible;
}
};
/* ********************************************************************** */
/* JQM Collapsible-Set */
/* ********************************************************************** */
/**
* Generate a JQM collapsibleset
*
* {
* "generate": "collapsibleset",
* "property_dict": {
* "direction": "horizontal"
* "id": null,
* "class_list": null,
* "inset": true,
* "corners": null,
* "theme": null,
* "content_theme": null,
* "collapsed_icon": "plus",
* "expanded_icon": "minus",
* "iconpos": "left",
* "mini": null
* },
* "children": []
* }
*
* @method collapsibleset
* @param {Object} spec Configuration for controlgroup
* @param {String} spec.generate Type of widget to generate
* @param {Object} spec.property_dict Configuration of widget
* @param {String} [spec.property_dict.direction=null] Accordeon/Tabs
* @param {String} [spec.property_dict.id=null] Id to set on widget
* @param {String} [spec.property_dict.class_list=null] Classes to add
* @param {Boolean} [spec.property_dict.inset=true] Inset (Round corners)
* @param {String} [spec.property_dict.theme=null] Theme to set on widget
* @param {Boolean} [spec.property_dict.corners=null] Corners to set
* @param {String} [spec.property_dict.content_theme=null] Theme for content
* @param {Boolean} [spec.property_dict.collapsed=true] Collapsible is open
* @param {String} [spec.property_dict.collapsed_icon="plus"] Opened Icon
* @param {String} [spec.property_dict.expanded_icon="minus"] Closed Icon
* @param {String} [spec.property_dict.iconpos="left"] Icon positioning
* @param {Boolean} [spec.property_dict.mini=false] Shrink widget size
* @param {Array} spec.children Content to display inside the widget
* @return collapsibleset
*/
factory.collapsibleset = function (spec) {
var config, prop, obj, j, l, element, set, corner;
if (spec === undefined) {
util.error("GeneratePopup: Missing configuration");
} else {
obj = {};
config = spec.property_dict || {};
corner = config.corners === false;
set = factory.element(
"div",
{"className": "ui-collapsible-set " +
"ui-group-theme " + (config.theme || "inherit") +
(corner ? " " : " ui-corner-all ")
},
{"data-enhanced": true}
);
// copy what is needed for children
for (prop in config) {
if (config.hasOwnProperty(prop)) {
switch (prop) {
case "direction":
case "class_list":
case "id":
break;
default:
obj[prop] = config[prop];
break;
}
}
}
if (spec.children) {
for (j = 0, l = spec.children.length; j < l; j += 1) {
element = spec.children[j];
element.property_dict = util.mergeObject(element.property_dict, obj);
set.appendChild(app.setContent(element));
}
}
return set;
}
};
/* ********************************************************************** */
/* JQM POPUP */
/* ********************************************************************** */
......@@ -3075,7 +3428,6 @@
if (i === 0 && closer) {
target.appendChild(closer);
}
target.appendChild(app.setContent(element));
panel.appendChild(target);
}
......@@ -3288,6 +3640,7 @@
if (spec.scheme.length > 0) {
for (i = 0; i < spec.scheme.length; i += 1) {
segment = spec.scheme[i];
area = segment.position === "center" ? 2 : 1;
container = wrap(area);
......@@ -3438,7 +3791,6 @@
return fragment;
};
/* ********************************************************************** */
/* JQM Controlgroup */
/* ********************************************************************** */
......@@ -3461,7 +3813,8 @@
* @return controlgroup
*/
factory.controlgroup = function (spec) {
var config, group, direction, controls, i, element, order, state;
var config, group, direction, controls, i, element, order, state, fragment,
label;
if (spec === undefined) {
util.error("Generate Controlgroup: Missing config");
......@@ -3470,6 +3823,30 @@
direction = config.direction || "vertical";
state = spec.set_state;
if (config.label) {
// NOTE: this will always wrap!!!
fragment = factory.element(
"div",
{"className": "ui-fieldcontain translate"},
{
"data-i18n": "[title]" + config.label.title_i18n,
"title": config.label.title
}
);
label = factory.element(
"div",
{"className": "ui-controlgroup-label"},
{"role": "heading"}
);
label.appendChild(factory.element(
"legend",
{},
{"data-i18n": config.label.text_i18n},
{"text": config.label.text}
));
fragment.appendChild(label);
}
// group
group = factory.element(
"div",
......@@ -3532,6 +3909,14 @@
}
}
}
// flag radio and checkboxes
switch (element.attributes.type) {
case "radio":
case "checkbox":
element.form_treatment = true;
break;
}
controls.appendChild(app.setContent(element));
}
}
......@@ -3539,7 +3924,10 @@
// assemble
group.appendChild(controls);
return group;
if (fragment) {
fragment.appendChild(group);
}
return fragment || group;
}
};
......@@ -4060,7 +4448,7 @@
// loading a file with temporary ID
if (promises[j] === undefined) {
util.error("page: Element not found");
util.loader("", "status_dict.not_found", "ban-circle");
util.loader("", "status_dict.not_found", "ban");
}
if (util.testForString("panel", promises[j].className)) {
temp = promises[j];
......@@ -4512,9 +4900,10 @@
label_inside, label_target, element_target, action, clear,
theme, icon_string, input_type, need_text_node, container_class_list,
label_class_list, index, disabled, active, text, addLabel, readonly,
mask_set, mask, star, hidden_field, icon;
mask_set, mask, star, hidden_field, icon, skip_validation_message;
// don't make an input, make a span
// TODO: Not like this!
if (config.type === "span") {
return factory.element(
config.type,
......@@ -4536,7 +4925,7 @@
// helper
addLabel = function (config, label_class_list) {
// TODO: WTF find out why testing for r"equire" here...
// required elements need a marker
star = util.testForString("equire", config.direct.className, true);
return factory.element(
......@@ -4651,6 +5040,7 @@
if (input_type) {
switch (input_type) {
case "radio":
skip_validation_message = true;
container_class_list = "ui-" + config.attributes.type;
label_inside = true;
label_class_list = "ui-btn ui-corner-all ui-btn-inherit" +
......@@ -4659,6 +5049,7 @@
config.attributes["data-cacheval"] = true;
break;
case "checkbox":
skip_validation_message = true;
icon_string = factory.util.iconClasses(config, "checkbox");
container_class_list = "ui-" + config.attributes.type;
label_inside = true;
......@@ -4701,7 +5092,7 @@
}
// assemble
container_class_list += " ui-corner-all ui-shadow-inset" +
container_class_list += (skip_validation_message ? "" : " ui-corner-all ui-shadow-inset") +
disabled + readonly + (action || "") + (clear || "") + (theme || "");
// container
......@@ -4747,8 +5138,7 @@
// the input, otherwise CSS sibling selector will not work. This saves
// doing "invalid" handler with javascript
// NOTE: hidden fields & security fields get no label
if (need_text_node === undefined &&
!label_inside && !hidden_field &&
if (need_text_node === undefined && !hidden_field &&
config.logic.plain_element === undefined) {
label_target.appendChild(addLabel(config, label_class_list));
}
......@@ -4774,18 +5164,20 @@
));
// validation messages
element_target.appendChild(factory.element(
"span",
{"className": "ui-invalid-label"},
{},
{"text":"wrong"}
));
// checkbox radio gets label after input
if (label_inside) {
label_target.appendChild(addLabel(config, label_class_list));
if (!skip_validation_message) {
element_target.appendChild(factory.element(
"span",
{"className": "ui-invalid-label"},
{},
{"text":"wrong"}
));
}
// // checkbox radio gets label after input
// if (label_inside) {
// label_target.appendChild(addLabel(config, label_class_list));
// }
// reassemble select elements and add spans
if (element_reverse) {
container.appendChild(element_target);
......@@ -5068,29 +5460,40 @@
prefix = obj.id + "_";
valid = storage.validate(obj);
data = storage.parseForm(valid, prefix, true);
if (valid) {
data = storage.parseForm(valid, prefix, true);
// TODO: remove, used for update scope, which does not honor form definition
if (obj.force_data) {
config._force_data = true;
}
// TODO: remove, used for update scope, which does not honor form definition
if (obj.force_data) {
config._force_data = true;
}
if (form.identifier) {
// PUT > set id, method and view
data._id = form.identifier.value;
method = "put";
config._view = obj.state.view;
}
if (form.identifier) {
// PUT > set id, method and view
data._id = form.identifier.value;
method = "put";
config._view = obj.state.view;
}
if (pointer) {
config._action = action;
// in case action is triggerted that needs retrival from a reference
data._reference = obj.state.reference;
}
if (pointer) {
if (action) {
config._action = action;
} else {
// NOTE: this is the actual way to post to ERP5, will fail and only
// work on localStorage, so add portal_type
data.portal_type = obj.state.type;
}
// in case action is triggered that needs retrival from a reference
data._reference = obj.state.reference;
}
return RSVP.resolve(
app.storage_dict.items[method || "post"](data, config)
);
util.loader("", "status_dict.saving");
return RSVP.resolve(
app.storage_dict.items[method || "post"](data, config)
);
} else {
return RSVP.reject({"status": 400});
}
};
/**
......@@ -5127,6 +5530,7 @@
}
}
}
return container;
};
......@@ -6153,7 +6557,9 @@
}
// map
action = e.target.getAttribute("data-action");
fallback = e.currentTarget.getAttribute("data-action");
if (!action) {
fallback = e.currentTarget.getAttribute("data-action");
}
handler = map.actions[fallback || action];
if (fallback || action) {
......@@ -6358,7 +6764,8 @@
// TODO: id ... is crap
// TODO: passing empty state is not good, refactor, when adding state
app.generateActionObject = function (e) {
var element, pop, id, gadget;
var element, pop, id, gadget, is_doc;
switch (e.type) {
case "popupbeforeposition":
element = undefined;
......@@ -6366,7 +6773,12 @@
gadget = e.target;
break;
default:
element = e.currentTarget || e.target || e;
is_doc = e.currentTarget === document;
if (is_doc) {
element = e.target || e;
} else {
element = e.currentTarget || e.target || e;
}
pop = element.getAttribute("data-rel") === null;
id = pop ?
(element.getAttribute("data-reference") || util.getPage().getAttribute("data-url")) :
......@@ -6837,64 +7249,6 @@
};
};
/**
* If no data is found in JIO, we need to load fake data for now
* @method fetchDataSample
* @param {object} reply Response from previous promise and pass-params
* @return {object} response object/promise
*/
storage.fetchSample = function (reply) {
var pass = reply.pass;
if (reply.response) {
if (util.parse(reply.response).data.total_rows === 0) {
return util.ajax(
{"url": "data/" + pass.config.portal_type_title + "_sample.json"}
)
.then(function (e) {
return {
"response": e.target.response,
"pass": pass
};
})
.fail(util.error);
}
}
return {
"pass": pass
};
};
/**
* Test if JIO has data of a certain portal_type
* @method testDataSample
* @param {object} reply Response from previous promise and pass-params
* @return {object} response object/promise
*/
app.testDataSample = function (reply) {
var pass = reply.pass;
if (pass.skip === undefined) {
if (reply.response) {
delete pass.needs_fields;
pass.fields = util.parse(reply.response);
}
// try to get 1 record for this portal type
if ((pass.create !== false || pass.purge) && pass.config.initial_query &&
(pass.no_auth || (pass.auth && pass.active_login))) {
return storage.fetch({
"storage": "items",
"query": app.generateQueryObject({"limit": [0, 1]}, pass.type),
"pass": pass
});
}
}
return {
"pass": pass
};
};
// ======================== SAMPLE DATA ==================================
/**
* Load field definitions if necessary
* @method fetchFields
......@@ -6992,7 +7346,7 @@
// TODO: remove!
if (content_dict === undefined) {
util.error("setContent: Page not found");
util.loader("", "status_dict.not_found", "ban-circle");
util.loader("", "status_dict.not_found", "ban");
};
switch (content_dict.generate) {
......@@ -7054,7 +7408,7 @@
switch (content_dict.type) {
case "input":
case "select":
return factory.formElement(content_dict, false);
return factory.formElement(content_dict, false, content_dict.form_treatment);
default:
return factory.element(
content_dict.type,
......
{
"global_dict": {
"cases": "Cases",
"nodes": "Nodes",
"pages": "Pages",
"results": "Results",
"reports": "Reports",
"suites": "Suites",
"edit": "Edit",
"delete": "Delete",
"dev": "Developemnt",
"test": "Test",
"test_environment": "UI test environment",
"test_cases": "<<nothing to see here>>",
"test_nodes": "<<nothing to see here>>",
"test_pages": "Create and run test pages locally",
"test_results": "<<nothing to see here>>",
"test_reports": "<<nothing to see here>>",
"test_suites": "<<nothing to see here>>",
"classic_login": "Or use the default login form",
"classic_slapos": "Login/Password",
"connection_status": "Connection",
......@@ -426,6 +440,16 @@
},
"test_page_dict": {
"text_dict": {
"metadata": "Metadata",
"history": "History",
"consistency": "Consistency",
"roles": "Local Roles",
"contents": "Contents",
"documents": "Related Documents",
"update": "Update",
"page": "Test Page",
"group": "Group Record Filter",
"filter": "Detailed Search",
"title": "Test Page Module",
"8":"8",
"16": "16",
......@@ -435,13 +459,21 @@
"empty": "No test pages found",
"search": "(I don't work but I should...) Search Test Pages.",
"long_title": "Long Title",
"add": "Add a new test page"
"add": "Add Test Page"
},
"field_dict": {
"reference": {
"title": "Reference",
"description": "The reference for this test page."
},
"text_content": {
"title": "Test Instructions",
"description": "The test instructions for this test page."
},
"content_type": {
"title": "Content Type",
"description": "The content type the test instructions are written in."
},
"title": {
"title": "Title",
"description": "The title of this test page."
......
......@@ -16,11 +16,93 @@
<script type="text/javascript" src="setup/jquery.testHelper.js"></script>
<script type="text/javascript">
var contentLoaded;
var contentLoaded, tableToJSON;
// stay idle
QUnit.config.autostart = false;
/**
* tabletojson
* jQuery plugin that reads an HTML table and returns a javascript object representing the values and columns of the table
*
* @author Daniel White
* @copyright Daniel White 2013
* @license MIT <https://github.com/lightswitch05/table-to-json/blob/master/MIT-LICENSE>
* @link https://github.com/lightswitch05/table-to-json
* @module tabletojson
* @version 0.6.0
*/
tableToJSON = function(table, opts) {
// Set options
var defaults = {
ignoreColumns: [],
onlyColumns: null,
ignoreHiddenRows: true,
headings: null
};
opts = $.extend(defaults, opts);
var notNull = function(value) {
if(value !== undefined && value !== null) {
return true;
}
return false;
};
var ignoredColumn = function(index) {
if( notNull(opts.onlyColumns) ) {
return $.inArray(index, opts.onlyColumns) === -1;
}
return $.inArray(index, opts.ignoreColumns) !== -1;
};
var arraysToHash = function(keys, values) {
var result = {};
$.each(values, function(index, value) {
if( index < keys.length ) {
result[ keys[index] ] = value;
}
});
return result;
};
var rowValues = function(row) {
var result = [];
$(row).children("td,th").each(function(cellIndex, cell) {
if( !ignoredColumn(cellIndex) ) {
var override = $(cell).data("override");
var value = $.trim($(cell).text());
result[ result.length ] = notNull(override) ? override : value;
}
});
return result;
};
var getHeadings = function(table) {
var firstRow = table.find("tr:first").first();
return notNull(opts.headings) ? opts.headings : rowValues(firstRow);
};
var construct = function(table, headings) {
var result = [];
table.children("tbody,*").children("tr").each(function(rowIndex, row) {
if( rowIndex !== 0 || notNull(opts.headings) ) {
if( $(row).is(":visible") || !opts.ignoreHiddenRows ) {
result[result.length] = arraysToHash(headings, rowValues(row));
}
}
});
return result;
};
// Run
var headings = getHeadings(table);
return construct(table, headings);
};
// DOMContentLoaded
contentLoaded = function (win, fn) {
var done = false,
......@@ -79,22 +161,71 @@
j = el.contentWindow.$;
helper = $.testHelper;
// ======================= start here ===========================
trigger = function (window, document, $, testHelper) {
trigger = function (window, d, $, testHelper) {
var i, instruction, madeJS, add;
var menu_button;
var string = '<table cellspacing="1" cellpadding="1" border="1">' +
'<thead>' +
'<tr class="title">' +
'<td colspan="3">Basic UI Interaction Test</td>' +
'</tr>' +
'</thead>' +
'<tbody>' +
'<tr>' +
'<td>Module</td>' +
'<td>We also want to test something else</td>' +
'<td></td>' +
'</tr>' +
'<tr>' +
'<td>waitForElementPresent</td>' +
'<td>//div[@id="global-panel"]</td>' +
'<td></td>' +
'</tr>' +
'<tr>' +
'<td>verifyText</td>' +
'<td>//div[@id="global-panel"]</td>' +
'<td>Menu</td>' +
'</tr>' +
'</tbody>' +
'</table>';
var tableToParse = tableToJSON( $(string), {"ignoreHiddenRows": false});
console.log(tableToParse)
madeJS = '';
for (i = 0; i < tableToParse.length; i += 1) {
instruction = tableToParse[i];
switch (instruction[i]) {
case "Module":
madeJS += 'module("' + instruction.Selector + '");';
break;
}
}
madeJS += ' asyncTest( "Number 2", function() {expect( 0 ); start(); });';
// inject
add = d.createElement("script");
add.type = "text/javascript";
add.appendChild(d.createTextNode(madeJS));
document.body.appendChild(add);
console.log("so...")
QUnit.start();
$.testHelper = testHelper;
// just a dummy....
window.setTimeout(function () {
QUnit.start();
// ========================
module( "Network Section" );
asyncTest( "Navigate to network page", function() {
var network_page, menu_button;
expect( 8 );
expect( 1 );
$.testHelper.pageSequence([
function() {
......@@ -102,6 +233,7 @@
ok( menu_button.length === 1, "Menu button exists, application init rendered correctly");
menu_button.trigger( "click" );
},
/*
function() {
setTimeout(function() {
ok( $("#global-panel").hasClass("ui-panel-open"), "panel opens when clicking menu button" );
......@@ -149,7 +281,7 @@
}
ok(test_value, "network has been generated and the name is matching what was entered before");
}, 1000);
},
},*/
function() {
start();
}
......
// > Something to load files with a specified format declareJS, HTML...
// > Mapper to convert HTML into JSON
// > Mapper to convert JSON to test instructions based on syntax of Qunit
// > Something to load multiple files with promises
// > A sequence loader which handles promises without pausing
// > The possibility to make snapshots and store them somewhere
// > The possibility to generate a documentation with the texts of test
// modules and asyncTests, so every test module will have a corresponding
// bullet on a slide with a screenshot...
// > convert the whole thing to a HTML presentation = view presentation
// > view = PDF
// > an easy to enter HTML generator in the proto UI, which allows to build
// test tables same as in ERP5 currently.
// > the whole thing probably needs to be stored in JIO... so we can stor
// in local and in ERP5 storage
// > view ... XML?
// > view ... default = QUNIT runner
// > Also we need to parse HTML old and new, old having <section> and <test>
// > new I would prefer just having tests. For old, how do we get screenshots?
//
(function( $ ) {
$.testHelper.extend({
"parseTest": function (str) {
var i, l, elem, parsed = $.parseHTML(str);
l = parsed.length;
for (i = 0; i < l; i += 1) {
elem = parsed[i];
switch (elem.tagName) {
case "TEST":
break;
case "SECTION":
break;
}
}
}
});
})(jQuery);
(function (document, window, RSVP, undefined) {
"use strict";
// thx - jQuery parseHTML - http://api.jquery.com/jquery.parsehtml/
var rhtml = /<|&#?\w+;/;
var rtagName = /<([\w:]+)/;
var rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi;
var wrapMap = {
"option": [ 1, "<select multiple='multiple'>", "</select>" ],
"thead": [ 1, "<table>", "</table>" ],
"col": [ 2, "<table><colgroup>", "</colgroup></table>" ],
"tr": [ 2, "<table><tbody>", "</tbody></table>" ],
"td": [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ],
"_default": [ 0, "", "" ]
};
// Support: IE 9
wrapMap.optgroup = wrapMap.option;
wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
wrapMap.th = wrapMap.td;
function buildFragment = function (elems) {
var fragment, node_list, i, j, l, elem, tag, tmp, wrap, merge, testContain;
fragment = document.createDocumentFragment();
node_list = [];
l = elems.length;
merge = function( first, second ) {
var len, j, i;
len = +second.length,
i = first.length;
for (j = 0; j < len; j += 1 ) {
first[ i += 1 ] = second[ j ];
}
first.length = i;
return first;
};
testContain =
for (i = 0; i < l; i += 0 {
elem = elems[i];
if (elem && rhtml.test(elem)) {
tag = ( rtagName.exec( elem ) || [ "", "" ] )[ 1 ].toLowerCase();
switch (tag) {
case "section":
case "test":
// Deserialize a standard representation
tmp = tmp || fragment.appendChild( document.createElement("div") );
wrap = wrapMap[ tag ] || wrapMap._default;
tmp.innerHTML = wrap[ 1 ] + elem.replace( rxhtmlTag, "<$1></$2>" ) + wrap[ 2 ];
};
}
}
// Descend through wrappers to the right content
j = wrap[ 0 ];
while ( j-- ) {
tmp = tmp.lastChild;
}
// Support: QtWebKit
// merge because push.apply(_, arraylike) throws
merge(node_list, tmp.childNodes);
// Remember the top-level container
tmp = fragment.firstChild;
// Support: Webkit, IE
tmp.textContent = "";
// Remove wrapper from fragment
fragment.textContent = "";
i = 0;
while ((elem = nodes[ i += 1 ])) {
contains = jQuery.contains( elem.ownerDocument, elem );
// Append to fragment
tmp = getAll( fragment.appendChild( elem ), "script" );
}
return fragment;
};
// thx - jQuery parseHTML - http://api.jquery.com/jquery.parsehtml/
function parseHTML = function(data) {
var element_list = [], parsed, rsingleTag = (/^<(\w+)\s*\/?>(?:<\/\1>|)$/);
if (!data || typeof data !== "string") {
return null;
}
// Single tag
parsed = rsingleTag.exec( data );
if ( parsed ) {
return [ context.createElement( parsed[1] ) ];
}
parsed = jQuery.buildFragment( [ data ], context, scripts );
return jQuery.merge( [], parsed.childNodes );
};
function cleanString = function (str) {
};
}(document, window, RSVP, Channel));
<table cellspacing="1" cellpadding="1" border="1" name="SELENIUM-TEST">
<thead>
<tr class="title">
<td colspan="3">SlapOS UI Basic Interaction</td>
</tr>
</thead>
<tbody>
<tr>
<td>store</td>
<td><urltool portal_url="" at=""></urltool></td>
<td>base_url</td>
</tr>
<tr>
<td>store</td>
<!-- ERP5TypeTestCase is the default for any UnitTest -->
<td></td>
<td>base_user</td>
</tr>
<tr>
<td>store</td>
<td></td>
<td>base_password</td>
</tr>
<tr>
<td>openAndWait</td>
<td>${base_url}</td>
<td></td>
</tr>
<tr>
<td>waitForPageToLoad</td>
<td><br></td>
<td></td>
</tr>
<tr>
<td>waitForElementPresent</td>
<td>//div[@id="global-panel"]</td>
<td></td>
</tr>
<tr>
<td>click</td>
<td>//a[.='Menu']</td>
<td></td>
</tr>
<tr>
<td>click</td>
<td>//h1[.='Networks']</td>
<td></td>
</tr>
<tr>
<td>waitForElementPresent</td>
<td>//a[.='Add network']</td>
<td></td>
</tr>
<tr>
<td>click</td>
<td>//a[.='Add network']</td>
<td></td>
</tr>
<tr>
<td>waitForElementPresent</td>
<td>//input[@id="network_new_title"]</td>
<td></td>
</tr>
<tr>
<td>type</td>
<td>id=network_new_title</td>
<td>Bazbam</td>
</tr>
<tr>
<td>pause</td>
<td>5000</td>
<td></td>
</tr>
<tr>
<td>click</td>
<td>//input[@type="submit"]</td>
<td></td>
</tr>
<tr>
<td>pause</td>
<td>10000</td>
<td></td>
</tr>
<tr>
<td>click</td>
<td>//a[@href="#networks"]</td>
<td></td>
</tr>
<tr>
<td>pause</td>
<td>5000</td>
<td></td>
</tr>
<tr>
<td>click</td>
<td>//a[.="Bazbam"]</td>
<td></td>
</tr>
<tr>
<td>pause</td>
<td>5000</td>
<td></td>
</tr>
<tr>
<td>click</td>
<td>//a[.="Delete"]</td>
<td></td>
</tr>
<tr>
<td>pause</td>
<td>5000</td>
<td></td>
</tr>
<tr>
<td>verifyText</td>
<td>//div[@class="info"]</td>
<td>1-3/3 Records</td>
</tr>
<tr>
<td>openAndWait</td>
<td>${base_url}</td>
<td></td>
</tr>
<tr>
<td>waitForPageToLoad</td>
<td><br></td>
<td></td>
</tr>
<tr>
<td>waitForElementPresent</td>
<td>//div[@id="global-panel"]</td>
<td></td>
</tr>
<tr>
<td>click</td>
<td>//a[.='Menu']</td>
<td></td>
</tr>
<tr>
<td>click</td>
<td>//h1[.='Person']</td>
<td></td>
</tr>
<tr>
<td>waitForElementPresent</td>
<td>//input[@id="user_sample_password"]</td>
<td></td>
</tr>
<tr>
<td>type</td>
<td>id=user_sample_password</td>
<td>topsecret</td>
</tr>
<tr>
<td>type</td>
<td>id=user_sample_default_fax_text</td>
<td>Foo</td>
</tr>
<tr>
<td>pause</td>
<td>5000</td>
<td></td>
</tr>
<tr>
<td>click</td>
<td>//input[@type="submit"]</td>
<td></td>
</tr>
<tr>
<td>pause</td>
<td>5000</td>
<td></td>
</tr>
<tr>
<td>click</td>
<td>//a[.='Home']</td>
<td></td>
</tr>
<tr>
<td>pause</td>
<td>2000</td>
<td></td>
</tr>
<tr>
<td>click</td>
<td>//a[.='Menu']</td>
<td></td>
</tr>
<tr>
<td>click</td>
<td>//h1[.='Person']</td>
<td></td>
</tr>
<tr>
<td>waitForElementPresent</td>
<td>//input[@id="user_sample_password"]</td>
<td></td>
</tr>
<tr>
<td>verifyAttribute</td>
<td>//input[@id="user_sample_default_fax_text"]@value</td>
<td>Foo</td>
</tr>
<tr>
<td>openAndWait</td>
<td>${base_url}</td>
<td></td>
</tr>
<tr>
<td>waitForPageToLoad</td>
<td><br></td>
<td></td>
</tr>
<tr>
<td>waitForElementPresent</td>
<td>//div[@id="global-panel"]</td>
<td></td>
</tr>
<tr>
<td>click</td>
<td>//a[.='Menu']</td>
<td></td>
</tr>
<tr>
<td>click</td>
<td>//h1[.='Invoices']</td>
<td></td>
</tr>
<tr>
<td>waitForElementPresent</td>
<td>//a[.="Download"]</td>
<td></td>
</tr>
<tr>
<td>openAndWait</td>
<td>${base_url}</td>
<td></td>
</tr>
<tr>
<td>waitForPageToLoad</td>
<td><br></td>
<td></td>
</tr>
<tr>
<td>waitForElementPresent</td>
<td>//div[@id="global-panel"]</td>
<td></td>
</tr>
<tr>
<td>click</td>
<td>//a[.='Menu']</td>
<td></td>
</tr>
<tr>
<td>click</td>
<td>//h1[.='Services']</td>
<td></td>
</tr>
<tr>
<td>waitForElementPresent</td>
<td>//a[.="Zabbix Agent"]</td>
<td></td>
</tr>
<tr>
<td>click</td>
<td>//a[.="Zabbix Agent"]</td>
<td></td>
</tr>
<tr>
<td>waitForElementPresent</td>
<td>//textarea[@id="service_instance_overview_xml_recipe"]</td>
<td></td>
</tr>
<tr>
<td>type</td>
<td>id=service_instance_overview_xml_recipe</td>
<td>Bar</td>
</tr>
<tr>
<td>pause</td>
<td>5000</td>
<td></td>
</tr>
<tr>
<td>click</td>
<td>//input[@type="submit"]</td>
<td></td>
</tr>
<tr>
<td>pause</td>
<td>5000</td>
<td></td>
</tr>
<tr>
<td>click</td>
<td>//a[.="Destroy"]</td>
<td></td>
</tr>
<tr>
<td>waitForElementPresent</td>
<td>//a[.="Install new service"]</td>
<td></td>
</tr>
<tr>
<td>verifyText</td>
<td>//div[@class="info"]</td>
<td>1-3/3 Records</td>
</tr>
<tr>
<td>openAndWait</td>
<td>${base_url}</td>
<td></td>
</tr>
<tr>
<td>waitForPageToLoad</td>
<td><br></td>
<td></td>
</tr>
<tr>
<td>waitForElementPresent</td>
<td>//div[@id="global-panel"]</td>
<td></td>
</tr>
<tr>
<td>click</td>
<td>//a[.='Menu']</td>
<td></td>
</tr>
<tr>
<td>click</td>
<td>//h1[.='Services']</td>
<td></td>
</tr>
<tr>
<td>pause</td>
<td>5000</td>
<td></td>
</tr>
<tr>
<td>click</td>
<td>//a[.="Install new service"]</td>
<td></td>
</tr>
<tr>
<td>pause</td>
<td>5000</td>
<td></td>
</tr>
<tr>
<td>type</td>
<td>id=search_software_list</td>
<td>Zabbix</td>
</tr>
<tr>
<td>pause</td>
<td>5000</td>
<td></td>
</tr>
<tr>
<td>click</td>
<td>link=[EXPERIMENTAL] Zabbix Agent</td>
<td></td></tr>
<tr>
<td>pause</td>
<td>5000</td>
<td></td>
</tr>
<tr>
<td>click</td>
<td>link=slapos-0.5</td>
<td></td>
</tr>
<tr>
<td>pause</td>
<td>5000</td>
<td></td>
</tr>
<tr>
<td>select</td>
<td>id=install_release_reference_computer</td>
<td>label=Cortana</td>
</tr>
<tr>
<td>pause</td>
<td>5000</td>
<td></td>
</tr>
<tr>
<td>click</td>
<td>//input[@value='Request']</td>
<td></td>
</tr>
<tr>
<td>pause</td>
<td>5000</td>
<td></td>
</tr>
<tr>
<td>click</td>
<td>//a[.="Menu"]</td>
<td></td>
</tr>
<tr>
<td>click</td>
<td>//h1[.='Services']</td>
<td></td>
</tr>
<tr>
<td>pause</td>
<td>5000</td>
<td></td>
</tr>
<tr>
<td>verifyText</td>
<td>//div[@class="info"]</td>
<td>1-5/5 Records</td>
</tr>
<tr>
<td>openAndWait</td>
<td>${base_url}</td>
<td></td>
</tr>
<tr>
<td>waitForPageToLoad</td>
<td><br></td>
<td></td>
</tr>
<tr>
<td>waitForElementPresent</td>
<td>//div[@id="global-panel"]</td>
<td></td>
</tr>
<tr>
<td>click</td>
<td>//a[.='Menu']</td>
<td></td>
</tr>
<tr>
<td>click</td>
<td>//h1[.='Servers']</td>
<td></td>
</tr>
<tr>
<td>pause</td>
<td>10000</td>
<td></td>
</tr>
<tr>
<td>type</td>
<td>id=search_server_list</td>
<td>Cortana</td>
</tr>
<tr>
<td>pause</td>
<td>2000</td>
<td></td>
</tr>
<tr>
<td>click</td>
<td>link=Cortana</td>
<td></td>
</tr>
<tr>
<td>waitForElementPresent</td>
<td>//a[.="Set Configuration"]</td>
<td></td>
</tr>
<tr>
<td>click</td>
<td>//a[.="Set Configuration"]</td>
<td></td>
</tr>
<tr>
<td>pause</td>
<td>2000</td>
<td></td>
</tr>
<tr>
<td>type</td>
<td>id=computer_sample_reference</td>
<td>Foo-123</td>
</tr>
<tr>
<td>type</td>
<td>id=computer_sample_translated_validation_state_title</td>
<td>Dead</td>
</tr>
<tr>
<td>pause</td>
<td>5000</td>
<td></td>
</tr>
<tr>
<td>click</td>
<td>//input[@type="submit"]</td>
<td></td>
</tr>
<tr>
<td>pause</td>
<td>5000</td>
<td></td>
</tr>
<tr>
<td>goBack</td>
<td></td>
<td></td>
</tr>
<tr>
<td>pause</td>
<td>5000</td>
<td></td>
</tr>
<tr>
<td>verifyAttribute</td>
<td>//input[@id="computer_status_reference"]@value</td>
<td>Foo-123</td>
</tr>
<tr>
<td>click</td>
<td>link=servers</td>
<td></td>
</tr>
<tr>
<td>pause</td>
<td>5000</td>
<td></td>
</tr>
<tr>
<td>type</td>
<td>id=search_server_list</td>
<td>Cortana</td>
</tr>
<tr>
<td>pause</td>
<td>2000</td>
<td></td>
</tr>
<tr>
<td>click</td>
<td>link=Dead</td>
<td></td>
</tr>
<tr>
<td>pause</td>
<td>5000</td>
<td></td>
</tr>
<tr>
<td>click</td>
<td>//a[.="Delete"]</td>
<td></td>
</tr>
<tr>
<td>pause</td>
<td>5000</td>
<td></td>
</tr>
<tr>
<td>verifyText</td>
<td>//div[@class="info"]</td>
<td>1-8/73 Records</td>
</tr>
<tr>
<td>openAndWait</td>
<td>${base_url}</td>
<td></td>
</tr>
<tr>
<td>waitForPageToLoad</td>
<td><br></td>
<td></td>
</tr>
<tr>
<td>waitForElementPresent</td>
<td>//div[@id="global-panel"]</td>
<td></td>
</tr>
<tr>
<td>click</td>
<td>//a[.='Menu']</td>
<td></td>
</tr>
<tr>
<td>click</td>
<td>//h1[.='Servers']</td>
<td></td>
</tr>
<tr>
<td>pause</td>
<td>10000</td>
<td></td>
</tr>
<tr>
<td>click</td>
<td>//a[.="Add Server"]</td>
<td></td>
</tr>
<tr>
<td>pause</td>
<td>5000</td>
<td></td>
</tr>
<tr>
<td>type</td>
<td>id=computer_new_title</td>
<td>Quantum</td>
</tr>
<tr>
<td>type</td>
<td>id=computer_new_description</td>
<td>I'm the fastest</td>
</tr>
<tr>
<td>pause</td>
<td>5000</td>
<td></td>
</tr>
<tr>
<td>click</td>
<td>//input[@type="submit"]</td>
<td></td>
</tr>
<tr>
<td>pause</td>
<td>5000</td>
<td></td>
</tr>
<tr>
<td>click</td>
<td>//a[.="Delete"]</td>
<td></td>
</tr>
<tr>
<td>pause</td>
<td>5000</td>
<td></td>
</tr>
<tr>
<td>verifyText</td>
<td>//div[@class="info"]</td>
<td>1-8/74 Records</td>
</tr>
</tbody>
</table>
/*! RenderJs */
/*
* renderJs - Generic Gadget library renderer.
* http://www.renderjs.org/documentation
*/
(function (document, window, RSVP, DOMParser, Channel, undefined) {
"use strict";
var gadget_model_dict = {},
javascript_registration_dict = {},
stylesheet_registration_dict = {},
gadget_loading_klass,
loading_gadget_promise,
renderJS;
/////////////////////////////////////////////////////////////////
// RenderJSGadget
/////////////////////////////////////////////////////////////////
function RenderJSGadget() {
if (!(this instanceof RenderJSGadget)) {
return new RenderJSGadget();
}
}
RenderJSGadget.prototype.title = "";
RenderJSGadget.prototype.interface_list = [];
RenderJSGadget.prototype.path = "";
RenderJSGadget.prototype.html = "";
RenderJSGadget.prototype.required_css_list = [];
RenderJSGadget.prototype.required_js_list = [];
RSVP.EventTarget.mixin(RenderJSGadget.prototype);
RenderJSGadget.ready_list = [];
RenderJSGadget.ready = function (callback) {
this.ready_list.push(callback);
return this;
};
/////////////////////////////////////////////////////////////////
// RenderJSGadget.declareMethod
/////////////////////////////////////////////////////////////////
RenderJSGadget.declareMethod = function (name, callback) {
this.prototype[name] = function () {
var context = this,
argument_list = arguments;
return new RSVP.Queue()
.push(function () {
return callback.apply(context, argument_list);
});
};
// Allow chain
return this;
};
RenderJSGadget
.declareMethod('getInterfaceList', function () {
// Returns the list of gadget prototype
return this.interface_list;
})
.declareMethod('getRequiredCSSList', function () {
// Returns a list of CSS required by the gadget
return this.required_css_list;
})
.declareMethod('getRequiredJSList', function () {
// Returns a list of JS required by the gadget
return this.required_js_list;
})
.declareMethod('getPath', function () {
// Returns the path of the code of a gadget
return this.path;
})
.declareMethod('getTitle', function () {
// Returns the title of a gadget
return this.title;
})
.declareMethod('getElement', function () {
// Returns the DOM Element of a gadget
if (this.element === undefined) {
throw new Error("No element defined");
}
return this.element;
})
.declareMethod('acquire', function () {
var gadget = this,
argument_list = arguments;
return new RSVP.Queue()
.push(function () {
var aq_dynamic = gadget.aq_dynamic;
if (aq_dynamic !== undefined) {
return aq_dynamic.apply(gadget, argument_list);
}
throw new renderJS.AcquisitionError("aq_dynamic is not defined");
})
.push(undefined, function (error) {
if (error instanceof renderJS.AcquisitionError) {
return gadget.aq_parent.apply(gadget, argument_list);
}
throw error;
});
});
/////////////////////////////////////////////////////////////////
// RenderJSEmbeddedGadget
/////////////////////////////////////////////////////////////////
// Class inheritance
function RenderJSEmbeddedGadget() {
if (!(this instanceof RenderJSEmbeddedGadget)) {
return new RenderJSEmbeddedGadget();
}
RenderJSGadget.call(this);
}
RenderJSEmbeddedGadget.ready_list = [];
RenderJSEmbeddedGadget.ready =
RenderJSGadget.ready;
RenderJSEmbeddedGadget.prototype = new RenderJSGadget();
RenderJSEmbeddedGadget.prototype.constructor = RenderJSEmbeddedGadget;
/////////////////////////////////////////////////////////////////
// privateDeclarePublicGadget
/////////////////////////////////////////////////////////////////
function privateDeclarePublicGadget(url, options) {
var gadget_instance;
if (options.element === undefined) {
options.element = document.createElement("div");
}
function loadDependency(method, url) {
return function () {
return method(url);
};
}
return new RSVP.Queue()
.push(function () {
return renderJS.declareGadgetKlass(url);
})
// Get the gadget class and instanciate it
.push(function (Klass) {
var i,
template_node_list = Klass.template_element.body.childNodes;
gadget_loading_klass = Klass;
gadget_instance = new Klass();
gadget_instance.element = options.element;
for (i = 0; i < template_node_list.length; i += 1) {
gadget_instance.element.appendChild(
template_node_list[i].cloneNode(true)
);
}
// Load dependencies if needed
return RSVP.all([
gadget_instance.getRequiredJSList(),
gadget_instance.getRequiredCSSList()
]);
})
// Load all JS/CSS
.push(function (all_list) {
var q = new RSVP.Queue(),
i;
// Load JS
for (i = 0; i < all_list[0].length; i += 1) {
q.push(loadDependency(renderJS.declareJS, all_list[0][i]));
}
// Load CSS
for (i = 0; i < all_list[1].length; i += 1) {
q.push(loadDependency(renderJS.declareCSS, all_list[1][i]));
}
return q;
})
.push(function () {
return gadget_instance;
});
}
/////////////////////////////////////////////////////////////////
// RenderJSIframeGadget
/////////////////////////////////////////////////////////////////
function RenderJSIframeGadget() {
if (!(this instanceof RenderJSIframeGadget)) {
return new RenderJSIframeGadget();
}
RenderJSGadget.call(this);
}
RenderJSIframeGadget.ready_list = [];
RenderJSIframeGadget.ready =
RenderJSGadget.ready;
RenderJSIframeGadget.prototype = new RenderJSGadget();
RenderJSIframeGadget.prototype.constructor = RenderJSIframeGadget;
/////////////////////////////////////////////////////////////////
// privateDeclareIframeGadget
/////////////////////////////////////////////////////////////////
function privateDeclareIframeGadget(url, options) {
var gadget_instance,
iframe,
node,
iframe_loading_deferred = RSVP.defer();
if (options.element === undefined) {
throw new Error("DOM element is required to create Iframe Gadget " +
url);
}
// Check if the element is attached to the DOM
node = options.element.parentNode;
while (node !== null) {
if (node === document) {
break;
}
node = node.parentNode;
}
if (node === null) {
throw new Error("The parent element is not attached to the DOM for " +
url);
}
gadget_instance = new RenderJSIframeGadget();
iframe = document.createElement("iframe");
// gadget_instance.element.setAttribute("seamless", "seamless");
iframe.setAttribute("src", url);
gadget_instance.path = url;
gadget_instance.element = options.element;
// Attach it to the DOM
options.element.appendChild(iframe);
// XXX Manage unbind when deleting the gadget
// Create the communication channel with the iframe
gadget_instance.chan = Channel.build({
window: iframe.contentWindow,
origin: "*",
scope: "renderJS"
});
// Create new method from the declareMethod call inside the iframe
gadget_instance.chan.bind("declareMethod", function (trans, method_name) {
gadget_instance[method_name] = function () {
var argument_list = arguments;
return new RSVP.Promise(function (resolve, reject) {
gadget_instance.chan.call({
method: "methodCall",
params: [
method_name,
Array.prototype.slice.call(argument_list, 0)],
success: function (s) {
resolve(s);
},
error: function (e) {
reject(e);
}
});
});
};
return "OK";
});
// Wait for the iframe to be loaded before continuing
gadget_instance.chan.bind("ready", function (trans) {
iframe_loading_deferred.resolve(gadget_instance);
return "OK";
});
gadget_instance.chan.bind("failed", function (trans, params) {
iframe_loading_deferred.reject(params);
return "OK";
});
gadget_instance.chan.bind("trigger", function (trans, params) {
return gadget_instance.trigger(params.event_name, params.options);
});
gadget_instance.chan.bind("acquire", function (trans, params) {
gadget_instance.acquire.apply(gadget_instance, params)
.then(function (g) {
trans.complete(g);
}).fail(function (e) {
trans.error(e.toString());
});
trans.delayReturn(true);
});
return RSVP.any([
iframe_loading_deferred.promise,
// Timeout to prevent non renderJS embeddable gadget
// XXX Maybe using iframe.onload/onerror would be safer?
RSVP.timeout(5000)
]);
}
/////////////////////////////////////////////////////////////////
// RenderJSGadget.declareGadget
/////////////////////////////////////////////////////////////////
RenderJSGadget.prototype.declareGadget = function (url, options) {
var queue,
parent_gadget = this,
previous_loading_gadget_promise = loading_gadget_promise;
if (options === undefined) {
options = {};
}
if (options.sandbox === undefined) {
options.sandbox = "public";
}
// Change the global variable to update the loading queue
queue = new RSVP.Queue()
// Wait for previous gadget loading to finish first
.push(function () {
return previous_loading_gadget_promise;
})
.push(undefined, function () {
// Forget previous declareGadget error
return;
})
.push(function () {
var method;
if (options.sandbox === "public") {
method = privateDeclarePublicGadget;
} else if (options.sandbox === "iframe") {
method = privateDeclareIframeGadget;
} else {
throw new Error("Unsupported sandbox options '" +
options.sandbox + "'");
}
return method(url, options);
})
// Set the HTML context
.push(function (gadget_instance) {
var i;
// Define aq_parent to reach parent gadget
gadget_instance.aq_parent = function (method_name, argument_list) {
return parent_gadget.acquire(method_name, argument_list);
};
// Drop the current loading klass info used by selector
gadget_loading_klass = undefined;
// Trigger calling of all ready callback
function ready_wrapper() {
return gadget_instance;
}
for (i = 0; i < gadget_instance.constructor.ready_list.length;
i += 1) {
// Put a timeout?
queue.push(gadget_instance.constructor.ready_list[i]);
// Always return the gadget instance after ready function
queue.push(ready_wrapper);
}
return gadget_instance;
})
.push(undefined, function (e) {
// Drop the current loading klass info used by selector
// even in case of error
gadget_loading_klass = undefined;
throw e;
});
loading_gadget_promise = queue;
return loading_gadget_promise;
};
/////////////////////////////////////////////////////////////////
// renderJS selector
/////////////////////////////////////////////////////////////////
renderJS = function (selector) {
var result;
if (selector === window) {
// window is the 'this' value when loading a javascript file
// In this case, use the current loading gadget constructor
result = gadget_loading_klass;
}
if (result === undefined) {
throw new Error("Unknown selector '" + selector + "'");
}
return result;
};
/////////////////////////////////////////////////////////////////
// renderJS.AcquisitionError
/////////////////////////////////////////////////////////////////
renderJS.AcquisitionError = function (message) {
this.name = "AcquisitionError";
if ((message !== undefined) && (typeof message !== "string")) {
throw new TypeError('You must pass a string.');
}
this.message = message || "Acquisition failed";
};
renderJS.AcquisitionError.prototype = new Error();
renderJS.AcquisitionError.prototype.constructor =
renderJS.AcquisitionError;
/////////////////////////////////////////////////////////////////
// renderJS.declareJS
/////////////////////////////////////////////////////////////////
renderJS.declareJS = function (url) {
// Prevent infinite recursion if loading render.js
// more than once
var result;
if (javascript_registration_dict.hasOwnProperty(url)) {
result = RSVP.resolve();
} else {
result = new RSVP.Promise(function (resolve, reject) {
var newScript;
newScript = document.createElement('script');
newScript.type = 'text/javascript';
newScript.src = url;
newScript.onload = function () {
javascript_registration_dict[url] = null;
resolve();
};
newScript.onerror = function (e) {
reject(e);
};
document.head.appendChild(newScript);
});
}
return result;
};
/////////////////////////////////////////////////////////////////
// renderJS.declareCSS
/////////////////////////////////////////////////////////////////
renderJS.declareCSS = function (url) {
// https://github.com/furf/jquery-getCSS/blob/master/jquery.getCSS.js
// No way to cleanly check if a css has been loaded
// So, always resolve the promise...
// http://requirejs.org/docs/faq-advanced.html#css
var result;
if (stylesheet_registration_dict.hasOwnProperty(url)) {
result = RSVP.resolve();
} else {
result = new RSVP.Promise(function (resolve, reject) {
var link;
link = document.createElement('link');
link.rel = 'stylesheet';
link.type = 'text/css';
link.href = url;
link.onload = function () {
stylesheet_registration_dict[url] = null;
resolve();
};
link.onerror = function (e) {
reject(e);
};
document.head.appendChild(link);
});
}
return result;
};
/////////////////////////////////////////////////////////////////
// renderJS.declareGadgetKlass
/////////////////////////////////////////////////////////////////
renderJS.declareGadgetKlass = function (url) {
var result,
xhr;
function parse() {
var tmp_constructor,
key,
parsed_html;
if (!gadget_model_dict.hasOwnProperty(url)) {
// Class inheritance
tmp_constructor = function () {
RenderJSGadget.call(this);
};
tmp_constructor.ready_list = [];
tmp_constructor.declareMethod =
RenderJSGadget.declareMethod;
tmp_constructor.ready =
RenderJSGadget.ready;
tmp_constructor.prototype = new RenderJSGadget();
tmp_constructor.prototype.constructor = tmp_constructor;
tmp_constructor.prototype.path = url;
// https://developer.mozilla.org/en-US/docs/HTML_in_XMLHttpRequest
// https://developer.mozilla.org/en-US/docs/Web/API/DOMParser
// https://developer.mozilla.org/en-US/docs/Code_snippets/HTML_to_DOM
tmp_constructor.template_element =
(new DOMParser()).parseFromString(xhr.responseText, "text/html");
parsed_html = renderJS.parseGadgetHTMLDocument(
tmp_constructor.template_element
);
for (key in parsed_html) {
if (parsed_html.hasOwnProperty(key)) {
tmp_constructor.prototype[key] = parsed_html[key];
}
}
gadget_model_dict[url] = tmp_constructor;
}
return gadget_model_dict[url];
}
function resolver(resolve, reject) {
function handler() {
var tmp_result;
try {
if (xhr.readyState === 0) {
// UNSENT
reject(xhr);
} else if (xhr.readyState === 4) {
// DONE
if ((xhr.status < 200) || (xhr.status >= 300) ||
(!/^text\/html[;]?/.test(
xhr.getResponseHeader("Content-Type") || ""
))) {
reject(xhr);
} else {
tmp_result = parse();
resolve(tmp_result);
}
}
} catch (e) {
reject(e);
}
}
xhr = new XMLHttpRequest();
xhr.open("GET", url);
xhr.onreadystatechange = handler;
xhr.setRequestHeader('Accept', 'text/html');
xhr.withCredentials = true;
xhr.send();
}
function canceller() {
if ((xhr !== undefined) && (xhr.readyState !== xhr.DONE)) {
xhr.abort();
}
}
if (gadget_model_dict.hasOwnProperty(url)) {
// Return klass object if it already exists
result = RSVP.resolve(gadget_model_dict[url]);
} else {
// Fetch the HTML page and parse it
result = new RSVP.Promise(resolver, canceller);
}
return result;
};
/////////////////////////////////////////////////////////////////
// renderJS.clearGadgetKlassList
/////////////////////////////////////////////////////////////////
// For test purpose only
renderJS.clearGadgetKlassList = function () {
gadget_model_dict = {};
javascript_registration_dict = {};
stylesheet_registration_dict = {};
};
/////////////////////////////////////////////////////////////////
// renderJS.parseGadgetHTMLDocument
/////////////////////////////////////////////////////////////////
renderJS.parseGadgetHTMLDocument = function (document_element) {
var settings = {
title: "",
interface_list: [],
required_css_list: [],
required_js_list: []
},
i,
element;
if (document_element.nodeType === 9) {
settings.title = document_element.title;
for (i = 0; i < document_element.head.children.length; i += 1) {
element = document_element.head.children[i];
if (element.href !== null) {
// XXX Manage relative URL during extraction of URLs
// element.href returns absolute URL in firefox but "" in chrome;
if (element.rel === "stylesheet") {
settings.required_css_list.push(element.getAttribute("href"));
} else if (element.type === "text/javascript") {
settings.required_js_list.push(element.getAttribute("src"));
} else if (element.rel === "http://www.renderjs.org/rel/interface") {
settings.interface_list.push(element.getAttribute("href"));
}
}
}
} else {
throw new Error("The first parameter should be an HTMLDocument");
}
return settings;
};
/////////////////////////////////////////////////////////////////
// global
/////////////////////////////////////////////////////////////////
window.rJS = window.renderJS = renderJS;
window.RenderJSGadget = RenderJSGadget;
window.RenderJSEmbeddedGadget = RenderJSEmbeddedGadget;
window.RenderJSIframeGadget = RenderJSIframeGadget;
///////////////////////////////////////////////////
// Bootstrap process. Register the self gadget.
///////////////////////////////////////////////////
function bootstrap() {
var url = window.location.href,
tmp_constructor,
root_gadget,
declare_method_count = 0,
embedded_channel,
notifyReady,
notifyDeclareMethod,
notifyTrigger,
gadget_ready = false;
// Create the gadget class for the current url
if (gadget_model_dict.hasOwnProperty(url)) {
throw new Error("bootstrap should not be called twice");
}
loading_gadget_promise = new RSVP.Promise(function (resolve, reject) {
if (window.self === window.top) {
// XXX Copy/Paste from declareGadgetKlass
tmp_constructor = function () {
RenderJSGadget.call(this);
};
tmp_constructor.declareMethod = RenderJSGadget.declareMethod;
tmp_constructor.ready_list = [];
tmp_constructor.ready = RenderJSGadget.ready;
tmp_constructor.prototype = new RenderJSGadget();
tmp_constructor.prototype.constructor = tmp_constructor;
tmp_constructor.prototype.path = url;
gadget_model_dict[url] = tmp_constructor;
// Create the root gadget instance and put it in the loading stack
root_gadget = new gadget_model_dict[url]();
// Stop acquisition on the original root gadget
// Do not put this on the klass, as their could be multiple instances
root_gadget.aq_parent = function (method_name) {
throw new renderJS.AcquisitionError(
"No gadget provides " + method_name
);
};
} else {
// Create the communication channel
embedded_channel = Channel.build({
window: window.parent,
origin: "*",
scope: "renderJS"
});
// Create the root gadget instance and put it in the loading stack
tmp_constructor = RenderJSEmbeddedGadget;
root_gadget = new RenderJSEmbeddedGadget();
// Bind calls to renderJS method on the instance
embedded_channel.bind("methodCall", function (trans, v) {
root_gadget[v[0]].apply(root_gadget, v[1]).then(function (g) {
trans.complete(g);
}).fail(function (e) {
trans.error(e.toString());
});
trans.delayReturn(true);
});
// Notify parent about gadget instanciation
notifyReady = function () {
if ((declare_method_count === 0) && (gadget_ready === true)) {
embedded_channel.notify({method: "ready"});
}
};
// Inform parent gadget about declareMethod calls here.
notifyDeclareMethod = function (name) {
declare_method_count += 1;
embedded_channel.call({
method: "declareMethod",
params: name,
success: function () {
declare_method_count -= 1;
notifyReady();
},
error: function () {
declare_method_count -= 1;
}
});
};
notifyDeclareMethod("getInterfaceList");
notifyDeclareMethod("getRequiredCSSList");
notifyDeclareMethod("getRequiredJSList");
notifyDeclareMethod("getPath");
notifyDeclareMethod("getTitle");
// Surcharge declareMethod to inform parent window
tmp_constructor.declareMethod = function (name, callback) {
var result = RenderJSGadget.declareMethod.apply(
this,
[name, callback]
);
notifyDeclareMethod(name);
return result;
};
notifyTrigger = function (eventName, options) {
embedded_channel.notify({
method: "trigger",
params: {
event_name: eventName,
options: options
}
});
};
// Surcharge trigger to inform parent window
tmp_constructor.prototype.trigger = function (eventName, options) {
var result = RenderJSGadget.prototype.trigger.apply(
this,
[eventName, options]
);
notifyTrigger(eventName, options);
return result;
};
// Define aq_parent to inform parent window
tmp_constructor.prototype.aq_parent = function (method_name,
argument_list) {
return new RSVP.Promise(function (resolve, reject) {
embedded_channel.call({
method: "acquire",
params: [
method_name,
argument_list
],
success: function (s) {
resolve(s);
},
error: function (e) {
reject(e);
}
});
});
};
}
gadget_loading_klass = tmp_constructor;
function init() {
// XXX HTML properties can only be set when the DOM is fully loaded
var settings = renderJS.parseGadgetHTMLDocument(document),
j,
key;
for (key in settings) {
if (settings.hasOwnProperty(key)) {
tmp_constructor.prototype[key] = settings[key];
}
}
tmp_constructor.template_element = document.createElement("div");
root_gadget.element = document.body;
for (j = 0; j < root_gadget.element.childNodes.length; j += 1) {
tmp_constructor.template_element.appendChild(
root_gadget.element.childNodes[j].cloneNode(true)
);
}
RSVP.all([root_gadget.getRequiredJSList(),
root_gadget.getRequiredCSSList()])
.then(function (all_list) {
var i,
js_list = all_list[0],
css_list = all_list[1],
queue;
for (i = 0; i < js_list.length; i += 1) {
javascript_registration_dict[js_list[i]] = null;
}
for (i = 0; i < css_list.length; i += 1) {
stylesheet_registration_dict[css_list[i]] = null;
}
gadget_loading_klass = undefined;
queue = new RSVP.Queue();
function ready_wrapper() {
return root_gadget;
}
queue.push(ready_wrapper);
for (i = 0; i < tmp_constructor.ready_list.length; i += 1) {
// Put a timeout?
queue.push(tmp_constructor.ready_list[i])
// Always return the gadget instance after ready function
.push(ready_wrapper);
}
queue.push(resolve, function (e) {
reject(e);
throw e;
});
return queue;
}).fail(function (e) {
reject(e);
/*global console */
console.error(e);
});
}
document.addEventListener('DOMContentLoaded', init, false);
});
if (window.self !== window.top) {
// Inform parent window that gadget is correctly loaded
loading_gadget_promise.then(function () {
gadget_ready = true;
notifyReady();
}).fail(function (e) {
embedded_channel.notify({method: "failed", params: e.toString()});
throw e;
});
}
}
bootstrap();
}(document, window, RSVP, DOMParser, Channel));
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment